Compare commits

...

64 Commits
0.2.0 ... 0.4.0

Author SHA1 Message Date
Joxit
6221958c78 Upgrade to v0.4.0
Merge tags and scripts; now it will be `docker-registry-ui.js` and `docker-registry-ui-static.js`
New sort for tags; will use numerical sort when it is possible
2018-07-24 00:05:45 +02:00
Jones Magloire
05c2cf2425 Merge pull request #46 from Joxit/new-tag-sort
New tag sort using digits
2018-07-23 20:36:04 +02:00
Joxit
82efd33c14 [new-tag-sort] Sort on numbers when there are instead of characters 2018-07-22 15:55:36 +02:00
Joxit
13936aadb1 [examples] Add ui-as-proxy example 2018-07-19 21:59:07 +02:00
Joxit
6bff056086 Add arm64v8 support 2018-07-18 22:59:07 +02:00
Joxit
b28fe68dcd Upgrade v0.3.10: Improve error messages 2018-07-15 20:56:16 +02:00
Joxit
d6523a4205 Upgrade v0.3.9: Fix typo 2018-06-21 23:14:20 +02:00
Joxit
3b5f5201a6 Upgrade to v0.3.8: Fix for REGISTRY_URL for #42 2018-06-20 22:17:35 +02:00
Joxit
cc624754b5 Upgrade to v0.3.7: Add copy to clipboard for images with tag and add sha256
Fixes: #42
2018-06-19 21:42:20 +02:00
Jones Magloire
b78fd358d7 Merge pull request #43 from Joxit/feat/copy-to-clipboard
[feat #42] Add copy to clipboard for images with tag and add sha256
2018-06-18 22:33:49 +02:00
Joxit
3430878e7d [feat #42] Add sha256 for images tag 2018-06-17 22:48:04 +02:00
Joxit
354d3159bd [feat #42] Add copy to clipboard for images with tag 2018-06-15 21:31:48 +02:00
Jones Magloire
86c46deb7e Merge pull request #41 from onizet/master
Fix '403 Request Entity Too Large'
2018-06-13 15:15:05 +02:00
Olivier Nizet
ace12d0ac8 Update with recommended values
- Disable client_max_body_size to avoid the error '403 Request Entity Too Large' which prevent uploading "big" images.
- Apply recommended settings by Docker (https://docs.docker.com/registry/recipes/nginx)
2018-06-13 13:23:09 +02:00
Joxit
4d1f47e808 Fix: #39 use _category?n=100000 for registries with many images 2018-06-09 00:09:32 +02:00
Jones Magloire
642afd695d Merge pull request #37 from Joxit/http-on-https-message
[feat #35] Better error message for http registries on https sites
2018-05-13 00:06:40 +02:00
Joxit
b5e07ea14f [feat #35] Better error message for http registries on https sites 2018-05-10 23:36:39 +02:00
Joxit
ad01f3ece9 Add arm32v7 support 2018-04-05 00:08:34 +02:00
Jones Magloire
4cae0dec8a Update README.md
Add example with `REGISTRY_TITLE`
2018-03-23 09:54:37 +01:00
Joxit
d5451f37c6 Add debian image version (joxit/docker-registry-ui:debian) 2018-03-18 23:58:36 +01:00
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
Joxit
36cbda1263 Update version 0.2.1: Fix issue #18
Fix `UNAUTHORIZED` error message popping up when browsing
2017-06-11 23:04:15 +02:00
Joxit
2d9b3886de [demo] Add analytics 2017-05-20 00:58:02 +02:00
Joxit
be553b0fb6 Add analytics 2017-05-04 21:20:33 +02:00
Joxit
deb401a418 Update Readme.md 2017-04-18 22:10:01 +02:00
Joxit
7adfd108da Add demo link 2017-04-17 21:44:52 +02:00
Jones Magloire
ded1a0be17 Set theme jekyll-theme-cayman 2017-04-13 11:45:20 +02:00
51 changed files with 1391 additions and 1112 deletions

4
.dockerignore Normal file
View File

@@ -0,0 +1,4 @@
*
!dist
!bin
!nginx

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
.project
node_modules
package-lock.json
registry-data

View File

@@ -1,4 +1,4 @@
# 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 published by
@@ -14,7 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
FROM nginx:alpine
MAINTAINER Jones MAGLOIRE @Joxit
LABEL maintainer="Jones MAGLOIRE @Joxit"
WORKDIR /usr/share/nginx/html/

View File

@@ -1,26 +0,0 @@
# Copyright (C) 2016 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 nginx
MAINTAINER Jones MAGLOIRE @Joxit
WORKDIR /usr/share/nginx/html/
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

1
Dockerfile.static Symbolic link
View File

@@ -0,0 +1 @@
static.dockerfile

View File

@@ -5,9 +5,11 @@
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/)
![screenshot](https://raw.github.com/Joxit/docker-registry-ui/master/screenshot.png "Screenshot of Docker Registry UI")
@@ -16,8 +18,15 @@ 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)
- Add Title when using REGISTRY_URL (see #28)
- 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)
## Getting Started
@@ -46,31 +55,43 @@ 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
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
# Alpine
docker build -t joxit/docker-registry-ui:latest docker-registry-ui
docker build -t joxit/docker-registry-ui:static -f docker-registry-ui/static.dockerfile docker-registry-ui
# Debian
docker build -t joxit/docker-registry-ui:debian -f docker-registry-ui/debian.dockerfile docker-registry-ui
docker build -t joxit/docker-registry-ui:static -f docker-registry-ui/debian-static.dockerfile 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
# Alpine
docker build -t joxit/docker-registry-ui:latest github.com/Joxit/docker-registry-ui
docker build -t joxit/docker-registry-ui:static -f static.dockerfile github.com/Joxit/docker-registry-ui
# Debian
docker build -t joxit/docker-registry-ui:debian -f debian.dockerfile github.com/Joxit/docker-registry-ui
docker build -t joxit/docker-registry-ui:debian-static -f debian-static.dockerfile 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
# Alpine
docker pull joxit/docker-registry-ui:latest
docker pull joxit/docker-registry-ui:static
# Debian
docker pull joxit/docker-registry-ui:debian
docker pull joxit/docker-registry-ui:debian-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
@@ -80,22 +101,38 @@ 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.
- `REGISTRY_TITLE`: Set a custom title for your user interface when using `REGISTRY_URL` (since 0.3.4)
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 -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/).
## 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:
@@ -107,7 +144,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:
@@ -115,7 +152,7 @@ storage:
enabled: true
```
And you need to add these HEADERS :
And you need to add these HEADERS:
```yml
http:

3
_config.yml Normal file
View File

@@ -0,0 +1,3 @@
title: Docker Registry v2 User Interface
google_analytics: UA-99119327-1
theme: jekyll-theme-cayman

26
arm32v7-static.dockerfile Normal file
View 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 arm32v7/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
arm32v7.dockerfile Normal file
View 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 arm32v7/nginx
LABEL maintainer="Jones MAGLOIRE @Joxit"
WORKDIR /usr/share/nginx/html/
COPY dist/ /usr/share/nginx/html/

26
arm64v8-static.dockerfile Normal file
View 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
View 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/

View File

@@ -1,13 +1,19 @@
#!/bin/bash
#!/bin/sh
$@
sed -i "s,\${URL},${URL}," 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
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

26
debian-static.dockerfile Normal file
View 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 nginx:latest
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
debian.dockerfile Normal file
View 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 nginx:latest
LABEL maintainer="Jones MAGLOIRE @Joxit"
WORKDIR /usr/share/nginx/html/
COPY dist/ /usr/share/nginx/html/

51
demo/index.html Normal file
View File

@@ -0,0 +1,51 @@
<!--
Copyright (C) 2016 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/>.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="../dist/vendor.css">
<link rel="stylesheet" href="../dist/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="../dist/scripts/vendor.js"></script>
<script src="../dist/scripts/tags.js"></script>
<script src="../dist/scripts/script.js"></script>
<script>
(function(i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function() {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date();
a = s.createElement(o),
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-99119327-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>

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/docker-registry-ui.js"></script></body></html>

File diff suppressed because one or more lines are too long

18
dist/scripts/docker-registry-ui.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,18 +0,0 @@
/*!
* docker-registry-ui
* Copyright (C) 2016 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._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 this.http._events)t.addEventListener(key,this.http._events[key]);for(key in this.http._headers)t.setRequestHeader(key,this.http._headers[key]);t.withCredentials=!0,t.open(this.http._method,this.http._url),t.send()}else 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.http=this,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");

View File

@@ -1,18 +0,0 @@
/*!
* docker-registry-ui
* Copyright (C) 2016 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._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 this.http._events)e.addEventListener(key,this.http._events[key]);for(key in this.http._headers)e.setRequestHeader(key,this.http._headers[key]);e.withCredentials=!0,e.open(this.http._method,this.http._url),e.send()}else 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.http=this,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(r){}return isNaN(e)?[]:""},registryUI.addServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);-1==r&&(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);-1!=r&&(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);-1!=r&&(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");

File diff suppressed because one or more lines are too long

18
dist/scripts/tags.js vendored

File diff suppressed because one or more lines are too long

551
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}.copy-to-clipboard{padding:12px 5px}.copy-to-clipboard a:hover{cursor:pointer}

56
dist/vendor.css vendored

File diff suppressed because one or more lines are too long

View 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 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
```

View 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:

View 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

View 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

View File

@@ -0,0 +1 @@
registry:$2y$11$1bmuJLK8HrQl5ACS/WeqRuJLUArUZfUcP2R23asmozEpfN76.pCHy

View 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:

View File

@@ -1,23 +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');
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');
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,
@@ -26,65 +50,45 @@ 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() {
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',
year: '2016',
year: '2016-2018',
organization: 'Jones Magloire @Joxit'
}))
.pipe(injectVersion())
.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'])
.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',
year: '2016',
year: '2016-2018',
organization: 'Jones Magloire @Joxit'
}))
.pipe(injectVersion())
.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',
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',
organization: 'Jones Magloire @Joxit'
}))
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'));
});
@@ -97,7 +101,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 +113,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', ['docker-registry-ui', 'vendor', 'docker-registry-ui-static', 'styles'], function() {
gulp.start();
});

43
nginx/default.conf Normal file
View File

@@ -0,0 +1,43 @@
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;
# disable any limits to avoid HTTP 413 for large image uploads
client_max_body_size 0;
# required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
chunked_transfer_encoding on;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#! 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 ${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.0",
"version": "0.4.0",
"scripts": {
"build": "./node_modules/gulp/bin/gulp.js build"
},
@@ -9,26 +9,26 @@
"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.4",
"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-inject-version": "^1.0.1",
"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": "^3.0.0",
"gulp-useref": "^3.1.5",
"riot": "^3.11.1",
"riot-mui": "^0.1.1",
"riot-route": "^3.1.3",
"stream-series": "^0.1.1",
"uglify-es": "^3.3.10"
}
}

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,50 +31,24 @@
</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 -->
<!-- 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>
<script src="tags/remove-image.tag" type="riot/tag"></script>
<script src="tags/copy-to-clipboard.tag" type="riot/tag"></script>
<script src="tags/add.tag" type="riot/tag"></script>
<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 -->
<script src="scripts/http.js"></script>
<script src="scripts/script.js"></script>
<!-- endbuild -->

View File

@@ -16,6 +16,8 @@
*/
function Http() {
this.oReq = new XMLHttpRequest();
this.oReq.hasHeader = Http.hasHeader;
this.oReq.getErrorMessage = Http.getErrorMessage;
this._events = {};
this._headers = {};
}
@@ -29,14 +31,16 @@ Http.prototype.addEventListener = function(e, f) {
self.oReq.addEventListener('loadend', function() {
if (this.status == 401) {
var req = new XMLHttpRequest();
for (key in this.http._events) {
req.addEventListener(key, this.http._events[key]);
req.open(self._method, self._url);
for (key in self._events) {
req.addEventListener(key, self._events[key]);
}
for (key in this.http._headers) {
req.setRequestHeader(key, this.http._headers[key]);
for (key in self._headers) {
req.setRequestHeader(key, self._headers[key]);
}
req.withCredentials = true;
req.open(this.http._method, this.http._url);
req.hasHeader = Http.hasHeader;
req.getErrorMessage = Http.getErrorMessage;
req.send();
} else {
f.bind(this)();
@@ -44,6 +48,15 @@ Http.prototype.addEventListener = function(e, f) {
});
break;
}
case 'load':
{
self.oReq.addEventListener('load', function() {
if (this.status !== 401) {
f.bind(this)();
}
});
break;
}
default:
{
self.oReq.addEventListener(e, function() {
@@ -66,6 +79,22 @@ Http.prototype.open = function(m, u) {
};
Http.prototype.send = function() {
this.oReq.http = this;
this.oReq.send();
};
Http.hasHeader = function(header) {
return this.getAllResponseHeaders().split('\n').some(function(h) {
return new RegExp('^' + header + ':', 'i').test(h);
});
};
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.';
} 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: Check your connection and your registry must have `Access-Control-Allow-Origin` header set to `' + window.location.origin + '`';
};

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,39 @@ 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');
window.addEventListener('DOMContentLoaded', function() {
riot.mount('*');
});

View File

@@ -18,10 +18,13 @@ var registryUI = {}
registryUI.url = function() {
return '${URL}';
};
registryUI.name = function() {
return '${REGISTRY_TITLE}' || registryUI.url();
};
registryUI.isImageRemoveActivated = true;
registryUI.catalog = {};
registryUI.taglist = {};
riot.mount('catalog');
riot.mount('taglist');
riot.mount('app');
window.addEventListener('DOMContentLoaded', function() {
riot.mount('*');
});

View File

@@ -15,31 +15,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
html > body {
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 +40,288 @@ 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;
}
.copy-to-clipboard {
padding: 12px 5px;
}
.copy-to-clipboard a:hover {
cursor: pointer;
}

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,160 @@
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 %%GULP_INJECT_VERSION%%</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}, 15000);
};
registryUI.errorSnackbar = function (message) {
return registryUI.snackbar(message, true);
}
registryUI.cleanName = function() {
var url = (registryUI.url() && registryUI.url().length > 0 && registryUI.url()) || window.location.host;
if (url) {
return url.startsWith('http') ? url.replace(/https?:\/\//, '') : url;
}
return '';
}
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)
});
registryUI.isDigit = function(char) {
return char >= '0' && char <= '9';
}
registryUI.DockerImage = function (name, tag) {
this.name = name;
this.tag = tag;
riot.observable(this);
this.on('get-size', function() {
if (this.size !== undefined) {
return this.trigger('size', this.size);
}
return this.fillInfo();
});
this.on('get-sha256', function() {
if (this.size !== undefined) {
return this.trigger('sha256', this.sha256);
}
return this.fillInfo();
});
};
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) {
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() {
if (this._fillInfoWaiting) {
return;
}
this._fillInfoWaiting = true;
var oReq = new Http();
var self = this;
oReq.addEventListener('loadend', function () {
if (this.status == 200 || this.status == 202) {
var response = JSON.parse(this.responseText);
self.size = response.layers.reduce(function (acc, e) {
return acc + e.size;
}, 0);
self.sha256 = response.config.digest;
self.trigger('size', self.size);
self.trigger('sha256', self.sha256);
} else if (this.status == 404) {
registryUI.errorSnackbar('Manifest for ' + self.name + ':' + self.tag + ' not found');
} else {
registryUI.snackbar(this.responseText);
}
});
oReq.open('GET', registryUI.url() + '/v2/' + self.name + '/manifests/' + self.tag);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
}
route.start(true);
</script>
</app>
</app>

View File

@@ -16,75 +16,54 @@
-->
<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(this.getErrorMessage(), true);
registryUI.catalog.repositories = [];
});
oReq.addEventListener('loadend', function () {
registryUI.catalog.loadend = true;
registryUI.catalog.instance.update();
});
oReq.open('GET', registryUI.url() + '/v2/_catalog');
oReq.open('GET', registryUI.url() + '/v2/_catalog?n=100000');
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>
<!-- End of tag -->
</catalog>
</catalog>

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>

View File

@@ -0,0 +1,34 @@
<!--
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/>.
-->
<copy-to-clipboard>
<input ref="input" style="display: none; width: 1px; height: 1px;" value="{ this.dockerCmd }">
<a onclick="{ this.copy }" title="Copy pull command.">
<i class="material-icons">content_copy</i>
</a>
<script type="text/javascript">
this.dockerCmd = 'docker pull ' + registryUI.cleanName() + '/' + opts.image.name + ':' + opts.image.tag;
this.copy = function () {
var copyText = this.refs['input'];
copyText.style.display = 'block';
copyText.select();
document.execCommand('copy');
copyText.style.display = 'none';
registryUI.snackbar('`' + this.dockerCmd + '` has been copied to clipboard.')
};
</script>
</copy-to-clipboard>

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

@@ -0,0 +1,37 @@
<!--
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 title="Compressed size of your image.">{ 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];
};
opts.image.on('size', function(size) {
self.size = size;
self.update();
});
opts.image.trigger('get-size');
</script>
</image-size>

27
src/tags/image-tag.tag Normal file
View File

@@ -0,0 +1,27 @@
<!--
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/>.
-->
<image-tag>
<div title="{ this.sha256 }">{ opts.image.tag }</div>
<script type="text/javascript">
var self = this;
opts.image.on('sha256', function(sha256) {
self.sha256 = sha256.substring(0, 19);
self.update();
});
opts.image.trigger('get-sha256');
</script>
</image-tag>

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

@@ -15,44 +15,43 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<remove-image>
<a href="#" onclick="registryUI.removeImage.remove('{ opts.name }', '{ opts.tag }')">
<i class="material-icons mdl-list__item-icon">delete</i>
<a href="#" title="This will delete the image." onclick="registryUI.removeImage.remove('{ opts.image.name }', '{ opts.image.tag }')">
<i class="material-icons">delete</i>
</a>
<script type="text/javascript">
registryUI.removeImage = registryUI.removeImage || {}
registryUI.removeImage.update = this.update;
registryUI.removeImage = registryUI.removeImage || {};
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,106 +16,89 @@
-->
<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></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="{ image in registryUI.taglist.tags }">
<td class="material-card-th-left">{ image.name }</td>
<td class="copy-to-clipboard">
<copy-to-clipboard image={ image }/>
</td>
<td><image-size image="{ image }" /></td>
<td><image-tag image="{ image }" /></td>
<td show="{ registryUI.isImageRemoveActivated }">
<remove-image image={ image }/>
</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();
registryUI.taglist.tags = registryUI.taglist.tags.map(function(tag) {
return new registryUI.DockerImage(registryUI.taglist.name, tag);
}).sort(registryUI.DockerImage.compare);
} 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(this.getErrorMessage(), 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) {
registryUI.taglist.tags.reverse();
registryUI.taglist.asc = false;
} else {
registryUI.taglist.tags.sort();
registryUI.taglist.tags.sort(registryUI.DockerImage.compare);
registryUI.taglist.asc = true;
}
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 -->

26
static.dockerfile Normal file
View 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 nginx:alpine
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