Compare commits

...

5 Commits
1.1.2 ... 1.2.0

Author SHA1 Message Date
Joxit
c01b440ff2 Release v1.2.0: Add multi-delete & demo canonical URL update 2019-04-23 08:06:41 +02:00
Joxit
63c310181b feat(multi-delete): Add multi select delete for tags 2019-04-23 08:04:38 +02:00
Vladimir Kozyrev
ab37bcfcef fix docker network name 2019-04-16 13:58:03 +02:00
Joxit
8e539be6ba Add quick example for issue #20
fixes: #20
2019-04-16 12:21:39 +02:00
Joxit
5b0ffc8eab Add quick exemple for issue #75 2019-04-15 20:30:50 +02:00
20 changed files with 323 additions and 18 deletions

View File

@@ -198,3 +198,12 @@ auth:
realm: basic-realm
path: /etc/docker/registry/htpasswd
```
## All examples
- [Use docker-registry-ui as a proxy (use REGISTRY_URL)](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-proxy)
- [Use docker-registry-ui as standalone (use URL)](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-standalone)
- [Use docker-registry-ui with traefik](https://github.com/Joxit/docker-registry-ui/tree/master/examples/traefik)
- [Use docker-registry-ui with docker registry and Amazon s3 (#75)](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-75)
- [FIX revproxy to registry does not work when published under non-root url (#73)](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-73)
- [Use docker-registry-ui with HTTPS (#20)](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-20)

View File

@@ -1,4 +1,5 @@
title: Docker Registry v2 User Interface
url: https://joxit.dev/docker-registry-ui/
google_analytics: UA-99119327-1
theme: jekyll-theme-cayman
author: Jones Magloire

View File

@@ -26,8 +26,8 @@
<meta property="og:locale" content="en_US" />
<meta name="description" content="This is the live demo for my project Docker Registry v2 web User Interface. Sources : https://github.com/Joxit/docker-registry-ui" />
<meta property="og:description" content="This is the live demo for my project Docker Registry v2 web User Interface. Sources : https://github.com/Joxit/docker-registry-ui" />
<link rel="canonical" href="http://joxit.dev/docker-registry-ui/demo/" />
<meta property="og:url" content="http://joxit.dev/docker-registry-ui/demo/" />
<link rel="canonical" href="https://joxit.dev/docker-registry-ui/demo/" />
<meta property="og:url" content="https://joxit.dev/docker-registry-ui/demo/" />
<meta property="og:site_name" content="Demo | Docker Registry UI" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@Joxit" />

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/style.css vendored

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
# Example for issue #20 (HTTPS supports)
This example will override the original nginx conf with one supporting HTTPS. You will need to rewrite all the project configuration (replaces `proxy_pass` with our value).
Generating a self signed certificate:
```
openssl req -newkey rsa:2048 -nodes -keyout nginx/privkey.pem -x509 -days 3650 -out nginx/fullchain.pem
```
The UI will be available here : https://localhost

View File

@@ -0,0 +1,27 @@
version: '2.0'
services:
registry:
image: registry:2.6.2
volumes:
- ./registry-data:/var/lib/registry
networks:
- registry-ui-net
ui:
image: joxit/docker-registry-ui:static
environment:
- REGISTRY_TITLE=My Private Docker Registry
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
- ./nginx/fullchain.pem:/etc/nginx/certs/fullchain.pem
- ./nginx/privkey.pem:/etc/nginx/certs/privkey.pem
ports:
- 80:80
- 443:443
depends_on:
- registry
networks:
- registry-ui-net
networks:
registry-ui-net:

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDYDCCAkigAwIBAgIJAKNtVPbuycx+MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTkwNDE2MDk1NzEzWhcNMjkwNDEzMDk1NzEzWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAuykmuGBPiNDWzxmqK7BQgJqDLWbAsf4769sI2gSMR0C6qd6WV6JJ+Rf+
y1auT2fA38cvJUjdPBEQCTxpE3Ce3e9nXYTITzze6OUCwewbdR/Cm+dHyR+M2YNP
SQrZI6p4NE1TwCHc0LVWfblAaWiylFPeWlFCVSg5hqKAkRh9PEcWBdN5vim3/8sC
16YmXWCERGPdFKYBN52ERJ+9h51ktMdns0LJVn+DLVSNWsiH76IMulHU64d9nZoL
kVhxohiOeP2ZuV7E+9RYDlaKObohclPz3RoOXUbr3zjjna+dqxI6mxCw5qms26RL
eBcQQA/EoqaAv+y+jCKqbCCcEgy27QIDAQABo1MwUTAdBgNVHQ4EFgQUDKyOzsPn
Tc6ZTTdnt8U59/j+3l8wHwYDVR0jBBgwFoAUDKyOzsPnTc6ZTTdnt8U59/j+3l8w
DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAmqHfSjO58FoJWJUM
2i0rcql0Y24XjZ92RdBQGLkvAhi+QxWBXNKibvpen2miv3fAeYmiFtIHQCuOCqCj
SSdQwb91D5WR9s21PILEWsOd1H0v4ZVHX2Z5Qv5f6Hk1DiTG/sZmzUqog74TtCpG
4m56/JYd4Mkk9raiWT9RKVTVnSHjM8h2zIMio14Nil4zO67G68jp1K0C1AM9npsf
cvQ2+2XAOEcQ7e3nCF4ppA3HdnCm8qbr8DM12KTs+nkncps/7u+3C5vv5TxI+BEz
b5Cs+HbLwPAphYp0CSK+sXiCUMA//mUAcMeYKq2/V4wufJlZEpBxogdttW7J4KJm
Num0pw==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,24 @@
server {
listen 443 ssl;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
root /usr/share/nginx/html;
location /v2 {
# Do not allow connections from docker 1.5 and earlier
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(.[0-9]-dev))|Go ).*$" ) {
return 404;
}
proxy_pass http://registry:5000;
}
}
server {
listen 80;
location / {
return 301 https://$host$request_uri;
}
}

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7KSa4YE+I0NbP
GaorsFCAmoMtZsCx/jvr2wjaBIxHQLqp3pZXokn5F/7LVq5PZ8Dfxy8lSN08ERAJ
PGkTcJ7d72ddhMhPPN7o5QLB7Bt1H8Kb50fJH4zZg09JCtkjqng0TVPAIdzQtVZ9
uUBpaLKUU95aUUJVKDmGooCRGH08RxYF03m+Kbf/ywLXpiZdYIREY90UpgE3nYRE
n72HnWS0x2ezQslWf4MtVI1ayIfvogy6UdTrh32dmguRWHGiGI54/Zm5XsT71FgO
Voo5uiFyU/PdGg5dRuvfOOOdr52rEjqbELDmqazbpEt4FxBAD8SipoC/7L6MIqps
IJwSDLbtAgMBAAECggEBAK7E+KFHXj22NkDiCGQPmrzcjA4DW4FalH3j5Vog0RVg
Pm6NqfpfU5BFdepPISqJCjRs/XtllSGYFU9ql/xNOCyqd+1+JsbHYqg74d1QKzut
0r5etEv9KDudQJZGiQmjD+hXJRPPCzHhg8iXCqzj1Y5o2sOgCb8XdtBgQoo7Qgbc
CG+3tytGPo33dotiFBUknrQRexTwgSWYXI89lI6fRSJlc8NyK7zp+mGbSopqGWHm
X6V8AI+XNuliIhTvOxGhw0maNEnds39SYHCYfLATjp9x6XVVp5mG7BJLkifC8Cob
IYQGfBwmEYbOTiNJ6oEgRZOZFPsLbqsPfPgTpHvIwUECgYEA7WfYek3DWkC7Ex3r
7hcZjBa8JMxPhgSMho/5F8zHGAf8MdEmXPYKi9tvhLeMJQwzzlN4RtX9zg0FJ5eL
tSgGHT/aRc2/9ZAvuG7gypNZlaAd+/SloYfKsGJQxFqLTfm288qyrRoOtBjhRMCI
lRmw5uYVV775cK741+lyD5xj/DkCgYEAydHb8mIt/IvCloVGzP8z4veIarEecYk3
UPw/wneZFZwGegGTsCwxox1uWVcO5CoNLhRo7622kZ6Mhsd83ySj8eQWpR1qoeMJ
8ti9c2FniZdtUwdFgu7GPgJq3DWTVQ0c0MTnyk/UbsfD/AKG0YK9T2sReteaPOUg
nohVutYZuFUCgYEAorXau7BSZKgaz3ZhfjQc0VO/rWTOWCcD/THt4i76gXDvm2Ei
bvI+ti42V3rJNZcQZqf0tm/x3Og1kTYfjZCZ6DAcNF1Y5D/nRPvRW2X0L0WnZ0j8
wCHmfE9V1c3MziuJBbv2DAfg7fRjaJCgy7fo88fb9uCv61gwuyKHh0WDjZkCgYB3
R89lLF3dm4TAjbjQxCyYgpBf7pr9o4nMFaphd2pE+Vhil7gAMb6Ml4J2zxuAAtKT
X4C917/FxR1tM048XF2BQ7uWjxJM5/EjVLJ0FSeqjJMStYOB5TnJwIgD6q5PYFad
lSMh0ZjOeMb+lUe1YD4fSDqmjfMc9pcW26E/sfa1mQKBgQDMRKH/R+yw6Nemu837
mwNVTiKtQoWS8jl8Gwox6o3cgrV/6szQaQz8oF9x829jFehYEMGYMX/8zPToyBCU
gRod6bcMmdLB8EQd7VI5L9/CeoZQmpVVZ1STNjUqscE/Gb98nCPNXTkVeAgtE1WS
AVhAAc+34wOxlAcjcXweBK69kg==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,7 @@
# Example for issue #75
Run this command `docker-compose up -d`, then you can push your images (e.g localhost:5000/alpine).
Be careful, the docker registry is using status codes 307 for each requests, that means you must configure your s3 to accept same requests as your private registry (that means `DELETE`, `Access-Control-Allow-Origin` and others).
This s3 server allow all requests.

View File

@@ -0,0 +1,32 @@
version: '2.0'
services:
registry:
image: registry:2.7
volumes:
- ./registry-data:/var/lib/registry
- ./registry-config/config.yml:/etc/docker/registry/config.yml
depends_on:
- s3-server
network_mode: host
ui:
image: joxit/docker-registry-ui:static
ports:
- 80:80
environment:
- URL=http://127.0.0.1:5000
- DELETE_IMAGES=true
depends_on:
- registry
network_mode: host
s3-server:
image: minio/minio:RELEASE.2019-04-09T01-22-30Z
volumes:
- ./s3-server-cmd:/bin/s3-server-cmd:ro
environment:
- MINIO_ACCESS_KEY=accessKey1
- MINIO_SECRET_KEY=verySecretKey1
- MINIO_REGION=us-east-1
network_mode: host
entrypoint: /bin/s3-server-cmd

View File

@@ -0,0 +1,37 @@
version: 0.1
log:
level: debug
fields:
service: registry
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
s3:
accesskey: accessKey1
secretkey: verySecretKey1
region: us-east-1
regionendpoint: http://127.0.0.1:9000
bucket: registry
encrypt: false
secure: false
v4auth: true
chunksize: 5242880
rootdirectory: /
http:
addr: 0.0.0.0:5000
headers:
X-Content-Type-Options: [nosniff]
Access-Control-Allow-Origin: ['http://127.0.0.1']
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']
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3

View File

@@ -0,0 +1,6 @@
#!/bin/sh
set -e
mkdir -p /data/registry
minio server /data

View File

@@ -5,7 +5,7 @@ services:
volumes:
- ./registry-data:/var/lib/registry
networks:
- docker-registry-ui
- registry-ui-net
ui:
image: joxit/docker-registry-ui:static
@@ -17,7 +17,7 @@ services:
depends_on:
- registry
networks:
- docker-registry-ui
- registry-ui-net
networks:
registry-ui-net:
registry-ui-net:

View File

@@ -1,6 +1,6 @@
{
"name": "docker-registry-ui",
"version": "1.1.2",
"version": "1.2.0",
"scripts": {
"build": "./node_modules/gulp/bin/gulp.js build"
},
@@ -14,7 +14,7 @@
"dependencies": {},
"devDependencies": {
"del": "^3.0.0",
"gulp": "^4.0",
"gulp": "^4.0.1",
"gulp-clean-css": "^4.0.0",
"gulp-concat": "^2.6.0",
"gulp-filter": "^5.1.0",

View File

@@ -395,10 +395,14 @@ select {
.remove-tag {
padding: 12px 5px;
width: 30px;
width: 66px;
text-align: center;
}
.remove-tag.delete {
padding: 7px 5px;
}
catalog material-card,
tag-history material-card {
min-height: auto;
@@ -469,4 +473,37 @@ catalog-element catalog-element.showing material-card,
catalog-element catalog-element.hide material-card {
margin-top: -50px;
opacity: 0;
}
remove-image {
width: 30px;
}
material-checkbox .label {
display: none;
}
taglist material-checkbox {
margin: auto;
width: 18px;
}
material-checkbox.indeterminate .checkbox .checkmark {
border-bottom: none;
}
material-checkbox.indeterminate .checkbox.checked .checkmark {
transform: rotate(90deg) scale(1);
-webkit-transform: rotate(90deg) scale(1);
-ms-transform: rotate(90deg) scale(1);
-moz-transform: rotate(90deg) scale(1);
-o-transform: rotate(90deg) scale(1);
}
material-checkbox .checkbox {
border-color: #777;
}
material-checkbox .checkbox.checked {
background-color: #777;
}

View File

@@ -15,13 +15,21 @@ 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/>.
-->
<remove-image>
<material-button waves-center="true" rounded="true" waves-color="#ddd" title="This will delete the image.">
<material-button waves-center="true" rounded="true" waves-color="#ddd" title="This will delete the image." hide="{ opts.multiDelete }">
<i class="material-icons">delete</i>
</material-button>
<material-checkbox show="{ opts.multiDelete }"></material-checkbox>
<script type="text/javascript">
const self = this;
this.on('update', function() {
if (!this.opts.multiDelete && this.tags['material-checkbox'].checked) {
this.tags['material-checkbox'].toggle();
}
});
this.on('mount', function() {
this.tags['material-button'].root.onclick = function() {
this.delete = this.tags['material-button'].root.onclick = function(ignoreError) {
const name = self.opts.image.name;
const tag = self.opts.image.tag;
const oReq = new Http();
@@ -39,7 +47,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
registryUI.taglist.display()
registryUI.snackbar('Deleting ' + name + ':' + tag + ' image. Run `registry garbage-collect config.yml` on your registry');
} else if (this.status == 404) {
registryUI.errorSnackbar('Digest not found');
ignoreError || registryUI.errorSnackbar('Digest not found');
} else {
registryUI.snackbar(this.responseText);
}
@@ -60,6 +68,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
};
this.tags['material-checkbox'].on('toggle', function() {
registryUI.taglist.instance.trigger('toggle-remove-image', this.checked);
});
});
</script>
</remove-image>

View File

@@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<taglist>
<!-- Begin of tag -->
<material-card ref="taglist-tag" class="taglist">
<material-card ref="taglist-tag" class="taglist" multi-delete={ this.multiDelete }>
<div class="material-card-title-action">
<material-button waves-center="true" rounded="true" waves-color="#ddd" onclick="registryUI.home();">
<i class="material-icons">arrow_back</i>
@@ -42,7 +42,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
onclick="registryUI.taglist.reverse();">Tag
</th>
<th class="show-tag-history">History</th>
<th class="remove-tag" show="{ registryUI.isImageRemoveActivated }"></th>
<th class={ 'remove-tag': true, delete: this.parent.toDelete > 0 } show="{ registryUI.isImageRemoveActivated }"><material-checkbox ref="remove-tag-checkbox" class="indeterminate" show={ this.toDelete === 0}></material-checkbox>
<material-button waves-center="true" rounded="true" waves-color="#ddd" title="This will delete selected images." onclick={ registryUI.taglist.bulkDelete } show={ this.toDelete > 0 }>
<i class="material-icons">delete</i>
</material-button></th>
</tr>
</thead>
<tbody>
@@ -64,14 +67,64 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<tag-history-button image={ image }/>
</td>
<td show="{ registryUI.isImageRemoveActivated }">
<remove-image image={ image }/>
<remove-image multi-delete={ this.opts.multiDelete } image={ image }/>
</td>
</tr>
</tbody>
</table>
</material-card>
<script>
registryUI.taglist.instance = this;
var self = registryUI.taglist.instance = this;
this.multiDelete = false;
this.toDelete = 0;
this.on('delete', function() {
if (!registryUI.isImageRemoveActivated || !this.multiDelete) {
return;
}
});
this.on('multi-delete', function() {
if (!registryUI.isImageRemoveActivated) {
return;
}
this.multiDelete = !this.multiDelete;
});
this.on('toggle-remove-image', function(checked) {
if (checked) {
this.toDelete++;
} else {
this.toDelete--;
}
if (this.toDelete <= 1) {
this.update();
}
})
registryUI.taglist.bulkDelete = function() {
if (self.multiDelete && self.toDelete > 0) {
var images = self.refs['taglist-tag'].tags['remove-image'];
if (!(images instanceof Array)) {
images = [images];
}
images.filter(function(img) {
return img.tags['material-checkbox'].checked;
}).forEach(function(img) {
img.delete(true);
});
}
}
this.on('mount', function() {
this.tags['material-card'].refs['remove-tag-checkbox'].on('toggle', function() {
registryUI.taglist.instance.multiDelete = this.checked;
registryUI.taglist.instance.update();
});
});
registryUI.taglist.display = function() {
registryUI.taglist.tags = [];
if (route.routeName == 'taglist') {