Compare commits

...

117 Commits
0.2.2 ... 0.6.1

Author SHA1 Message Date
Joxit
7446452b77 Release v0.6.1: Display image/tag count + button effect
Last release of the year !
2018-12-31 23:59:59 +01:00
Jones Magloire
d361068529 Merge pull request #65 from Joxit/feat/material-button
[material-button] Add material-button for all effective buttons
2018-12-30 22:29:24 +01:00
Joxit
b03f00ebe8 [material-button] Fix dialog buttons 2018-12-29 00:37:18 +01:00
Joxit
d0b7e7ddeb [material-button] Add material-button for all effective buttons 2018-12-28 20:44:25 +01:00
Jones Magloire
7366e709a4 Merge pull request #64 from Joxit/feat/docker-multi-stage
[docker-multi-stage] Add multi-stage-build for alpine version
2018-12-27 00:31:44 +01:00
Joxit
89e2782751 [docker-multi-stage] Add multi-stage-build for alpine version 2018-12-27 00:18:51 +01:00
Jones Magloire
3911310d89 Merge pull request #63 from Joxit/feat/image-tag-count
[feat #56] Add image and tags count
2018-12-25 23:23:33 +01:00
Joxit
d599c1c202 [feat #56] Add image and tags count 2018-12-21 23:55:23 +01:00
Joxit
1e185b4034 Release v0.6.0 new history page 2018-12-20 00:01:36 +01:00
Jones Magloire
03e4d6b8c5 Merge pull request #61 from Joxit/fix/image-history
[Fix #59] image history, use v2 manifest instead of v1
2018-12-19 21:32:41 +01:00
Joxit
fe2fcc1104 [fix-image-history] Add some query optimization 2018-12-18 23:25:57 +01:00
Joxit
581975b99e [fix-image-history] Create move history-elements in its own tag tag-history-element
- Add id and ExposedPort
- Better rendering for arrays
- Add more const instead of var
2018-12-17 00:38:25 +01:00
Joxit
ed1e928bf3 [fix-image-history] Add config values for top level history and improve element values rendering
Move icons to js mapping function instead of CSS
In dockerfile, author cand be randered through `MAINTAINER xxx` or `LABEL maintainer=xxx`
2018-12-16 00:06:35 +01:00
Joxit
ffd0a7c628 [fix-image-history] Move byteToSize in app.tag and use it in image history. Better render for created_by 2018-12-14 21:57:42 +01:00
Joxit
cb50dd42d8 [fix-image-history] Sort elements attributes (os, id, created...) 2018-12-13 20:05:28 +01:00
Joxit
5aaedfb0aa [fix-image-history] Move from v1 manifest to manifest v2 2018-12-12 22:41:20 +01:00
Joxit
7cb06d57ee [fix-image-history] Add blobs in DockerImage image, this will superseds current tag-history call 2018-12-11 23:09:22 +01:00
Joxit
a6b6c1531b [docker svg] Change docker logo for a simplified one
Size changed from 12.5Kio to 4.5Kio
2018-12-09 17:34:57 +01:00
Jones Magloire
e89a0112ae Merge pull request #58 from lennartblom/master
New Feature: Overview of tag history content
2018-12-08 15:49:55 +01:00
Joxit
4aa016090c Add more const 2018-12-07 19:46:08 +01:00
Lennart Blom
7163150cf5 Optimize GUI elements and HTTP logic for content preperation 2018-12-06 22:01:35 +01:00
Lennart Blom
c176a082d9 Change wording of headline 2018-12-06 21:13:13 +01:00
Lennart Blom
67ad46a851 Change wording of headline 2018-12-06 21:12:55 +01:00
Lennart Blom
06a11a706b Merge remote-tracking branch 'origin/master' 2018-12-06 21:10:51 +01:00
Lennart Blom
04259ab43d Change wording of headline 2018-12-06 21:10:12 +01:00
Jones Magloire
896031e894 Merge pull request #44 from onizet/master
Improve documentation: traefik sample
2018-12-05 21:46:08 +01:00
Joxit
8643fb16ff Add contributors section 2018-12-03 20:26:41 +01:00
Olivier Nizet
becf8bf887 Move documentation about traefik as a full runnable example 2018-12-03 20:12:33 +01:00
Lennart Blom
472b485455 Remove test logging 2018-12-03 16:48:21 +01:00
Olivier Nizet
637f7635dc Example behind Traefik 2018-12-03 16:45:07 +01:00
Lennart Blom
ae78b2d355 Fix navigation bug
The href="#" was troubling the view that the history appeared just for
a few milliseconds... the button with given onclick event does work now.
2018-12-03 16:36:51 +01:00
Lennart Blom
249d021152 Formate code 2018-12-02 19:15:27 +01:00
Lennart Blom
e3437daefe Use toLocaleString() instead of custom date format 2018-12-02 18:58:04 +01:00
Lennart Blom
246369fdec Move go function to taghistory-namespace within app.tag 2018-12-02 18:56:34 +01:00
Lennart Blom
b94a65d79b Remove space in front of function brackets 2018-12-02 18:52:34 +01:00
Lennart Blom
bf975cd29b Formate code 2018-12-01 21:29:40 +01:00
Lennart Blom
292336c1b6 Format code 2018-12-01 21:23:48 +01:00
Lennart Blom
6d849a9e95 Add information text about the history elements 2018-12-01 21:11:09 +01:00
Lennart Blom
86f78f7604 Remove instance usage of registryUI.taghistory 2018-12-01 21:00:27 +01:00
Lennart Blom
43af3ffdcf Remove logging 2018-12-01 20:52:53 +01:00
Lennart Blom
f364564d0e Remove logging 2018-12-01 20:51:55 +01:00
Lennart Blom
39cf28e562 Remove logging and format code 2018-12-01 20:50:55 +01:00
Lennart Blom
66fe613b6f Optimize format 2018-12-01 20:50:16 +01:00
Lennart Blom
cf883cbfc7 Optimize routing and code format 2018-12-01 20:49:59 +01:00
Lennart Blom
5bb7dfce7d Optimize date format 2018-12-01 20:32:33 +01:00
Lennart Blom
6d798ca75f Styling of history element headlines 2018-12-01 20:19:07 +01:00
Lennart Blom
f6bc4df11f Styling elements and getting structure into data view 2018-12-01 11:44:51 +01:00
Lennart Blom
8ab6ecbf19 Define dynamic parsing of history elements 2018-12-01 11:03:23 +01:00
Lennart Blom
c857bd8db6 Add functionality for new tag history view 2018-12-01 00:24:58 +01:00
Lennart Blom
fe5e962488 Format code 2018-12-01 00:24:28 +01:00
Lennart Blom
96a926652b Include tag-history-button 2018-12-01 00:23:38 +01:00
Lennart Blom
3f860cd0b5 Define taghistory object 2018-12-01 00:23:14 +01:00
Lennart Blom
3b1b6f2e72 Add new "tag-history" column for tag overview 2018-11-30 22:33:34 +01:00
Lennart Blom
20861bbb0d Add .idea directory to .ignore 2018-11-30 22:10:05 +01:00
Joxit
09b77201be [Fixes #52] image-date tag was missing in static version => v0.5.1 2018-11-23 22:22:17 +01:00
Joxit
2f5e0dd307 Upgrade to v0.5.0 2018-11-20 21:56:14 +01:00
Jones Magloire
08c5eaee7e Merge pull request #51 from Joxit/feat/creation-date
[feat #49] Add creation date to taglist
2018-11-18 12:09:51 +01:00
Joxit
4cb79a670f [feat #49] Add creation date to taglist 2018-11-16 22:07:49 +01:00
Joxit
5173110883 Add new example for https://github.com/Joxit/docker-registry-ui/issues/26#issuecomment-415995589 2018-08-25 23:33:33 +02:00
Joxit
d3e93f7064 Fix demo for v0.4.0, sorry 😕 2018-08-04 18:58:36 +02:00
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
80 changed files with 2297 additions and 1579 deletions

7
.dockerignore Normal file
View File

@@ -0,0 +1,7 @@
*
!dist
!bin
!nginx
!src
!package.json
!gulpfile.js

3
.gitignore vendored
View File

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

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
@@ -12,10 +12,22 @@
#
# 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 node:10-alpine AS builder
WORKDIR /usr/app
COPY package.json .
RUN yarn install
COPY . .
RUN yarn build
FROM nginx:alpine
MAINTAINER Jones MAGLOIRE @Joxit
LABEL maintainer="Jones MAGLOIRE @Joxit"
WORKDIR /usr/share/nginx/html/
COPY dist/ /usr/share/nginx/html/
COPY --from=builder /usr/app/dist/ /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,9 @@
This project aims to provide a user interface for your private docker registry v2.
There is no default registry on this UI, you should add your own with the UI.
You can manage more than one registry server.
All registry will be stored in the [local storage](https://en.wikipedia.org/wiki/Web_storage#Local_and_session_storage) of your browser.
All registries will be stored in the [local storage](https://en.wikipedia.org/wiki/Web_storage#Local_and_session_storage) of your browser.
This web user interface use [Riot](https://github.com/Riot/riot) the react-like user interface micro-library and [Material Design Lite](https://github.com/google/material-design-lite) components.
This web user interface uses [Riot](https://github.com/Riot/riot) the react-like user interface micro-library and [riot-mui](https://github.com/kysonic/riot-mui) components.
## [GitHub Page](https://joxit.github.io/docker-registry-ui) and [Live Demo](https://joxit.github.io/docker-registry-ui/demo/)
@@ -18,8 +18,18 @@ 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)
- Display image creation date (see #49)
- Display image history (see #58)
- Display image/tag count
## Getting Started
@@ -48,31 +58,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
@@ -82,22 +104,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/) or docker-registry-ui as standalone [here](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-standalone/).
## Using CORS
Your server should be configured to accept CORS.
If your docker registry does not need credentials, you will need to send this HEADER :
If your docker registry does not need credentials, you will need to send this HEADER:
Access-Control-Allow-Origin: '*'
If your docker registry need credentials, you will need to send these HEADERS :
If your docker registry need credentials, you will need to send these HEADERS:
```yml
http:
@@ -109,7 +147,7 @@ http:
## Using delete
For deleting images, you need to activate the delete feature in your registry :
For deleting images, you need to activate the delete feature in your registry:
```yml
storage:
@@ -117,7 +155,7 @@ storage:
enabled: true
```
And you need to add these HEADERS :
And you need to add these HEADERS:
```yml
http:

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/

View File

@@ -1,5 +1,5 @@
<!--
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
@@ -26,35 +26,9 @@
</head>
<body>
<!-- Always shows a header, even in smaller screens. -->
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<!-- Title --><span class="mdl-layout-title">Docker Registry UI</span>
<menu></menu>
</div>
</header>
<main class="mdl-layout__content">
<div class="page-content">
<app></app>
</div>
</main>
<change></change>
<add></add>
<remove></remove>
<footer class="mdl-mini-footer">
<div class="mdl-mini-footer__left-section">
<div class="mdl-logo">Docker Registry UI</div>
<ul class="mdl-mini-footer__link-list">
<li><a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a></li>
<li><a href="https://github.com/Joxit/docker-registry-ui/blob/master/LICENSE">Privacy &amp; Terms</a></li>
</ul>
</div>
</footer>
</div>
<app></app>
<script src="../dist/scripts/vendor.js"></script>
<script src="../dist/scripts/tags.js"></script>
<script src="../dist/scripts/script.js"></script>
<script src="../dist/scripts/docker-registry-ui.js"></script>
<script>
(function(i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
dist/images/docker-logo.svg vendored Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg2" x="0px" y="0px" viewBox="-5.724 -43.601 730 600" fill="#777"><path d="m595.942,422.343 c -7.543,0.119 -13.562,6.331 -13.443,13.875 0.119,7.544 6.332,13.562 13.875,13.443 7.495,-0.118 13.494,-6.254 13.445,-13.75 -0.085,-7.578 -6.297,-13.652 -13.875,-13.568 0,0 -10e-4,0 0,0 m 0,24.398 c -5.975,0.272 -11.039,-4.352 -11.311,-10.326 -0.271,-5.976 4.352,-11.04 10.327,-11.312 5.975,-0.271 11.039,4.352 11.311,10.327 0.01,0.19 0.013,0.382 0.011,0.573 0.204,5.723 -4.27,10.527 -9.992,10.731 -0.115,0.005 -0.23,0.007 -0.346,0.007"/><path d="m599.081,436.342 v -0.185 c 1.512,-0.292 2.65,-1.544 2.8,-3.076 0.057,-1.175 -0.432,-2.311 -1.323,-3.077 -1.445,-0.765 -3.076,-1.106 -4.707,-0.984 -1.743,-0.024 -3.484,0.12 -5.2,0.431 v 13.538 h 3.077 v -5.446 h 1.477 c 1.754,0 2.554,0.646 2.83,2.154 0.184,1.143 0.536,2.252 1.047,3.292 h 3.415 c -0.53,-1.062 -0.873,-2.207 -1.016,-3.385 -0.138,-1.473 -1.088,-2.744 -2.462,-3.292 m -3.723,-0.985 h -1.508 v -3.908 c 0.583,-0.069 1.172,-0.069 1.754,0 1.97,0 2.893,0.831 2.893,2.062 0,1.231 -1.415,2 -3.076,2"/><path d="M707.494,193.557c-1.938-1.539-20.029-15.199-58.181-15.199c-10.074,0.044-20.127,0.908-30.061,2.584 c-7.384-50.612-49.228-75.288-51.104-76.395l-10.245-5.908l-6.738,9.723c-8.438,13.061-14.598,27.459-18.214,42.582 c-6.831,28.891-2.677,56.027,11.999,79.226c-17.722,9.876-46.151,12.307-51.904,12.522H22.367 c-12.294,0.017-22.27,9.952-22.337,22.245c-0.549,41.234,6.437,82.222,20.614,120.946c16.214,42.521,40.336,73.842,71.719,93.01 c35.167,21.537,92.302,33.844,157.067,33.844c29.258,0.092,58.461-2.556,87.226-7.907c39.986-7.342,78.463-21.318,113.839-41.352 c29.149-16.88,55.383-38.354,77.688-63.596c37.29-42.213,59.505-89.226,76.026-131.007c2.215,0,4.431,0,6.584,0 c40.828,0,65.935-16.338,79.78-30.029c9.201-8.732,16.384-19.369,21.045-31.167l2.923-8.553L707.494,193.557z"/><path d="M65.995,228.909h63.073c3.042,0,5.507-2.466,5.507-5.507l0,0V167.22c0.017-3.042-2.435-5.521-5.476-5.538 c-0.01,0-0.021,0-0.031,0H65.995c-3.042,0-5.507,2.466-5.507,5.507c0,0.01,0,0.021,0,0.031v56.181 C60.488,226.443,62.953,228.909,65.995,228.909L65.995,228.909"/><path d="M152.913,228.909h63.073c3.042,0,5.507-2.466,5.507-5.507l0,0V167.22c0.017-3.042-2.435-5.521-5.477-5.538 c-0.01,0-0.021,0-0.031,0h-63.073c-3.059,0-5.538,2.479-5.538,5.538v56.181C147.392,226.448,149.866,228.909,152.913,228.909"/><path d="M241.153,228.909h63.073c3.042,0,5.507-2.466,5.507-5.507l0,0V167.22c0.017-3.042-2.435-5.521-5.477-5.538 c-0.01,0-0.021,0-0.031,0h-63.073c-3.042,0-5.507,2.466-5.507,5.507c0,0.01,0,0.021,0,0.031v56.181 C235.646,226.443,238.112,228.909,241.153,228.909L241.153,228.909"/><path d="M328.348,228.909h63.073c3.047,0,5.521-2.46,5.538-5.507V167.22c0-3.059-2.479-5.538-5.538-5.538l0,0h-63.073 c-3.042,0-5.507,2.466-5.507,5.507c0,0.01,0,0.021,0,0.031v56.181C322.841,226.443,325.307,228.909,328.348,228.909L328.348,228.909"/><path d="M152.913,148.083h63.073c3.046-0.017,5.507-2.492,5.507-5.538V86.364c0-3.042-2.466-5.507-5.507-5.507l0,0h-63.073 c-3.046,0-5.521,2.46-5.538,5.507v56.181C147.392,145.597,149.861,148.066,152.913,148.083"/><path d="M241.153,148.083h63.073c3.046-0.017,5.507-2.492,5.507-5.538V86.364c0-3.042-2.466-5.507-5.507-5.507l0,0h-63.073 c-3.042,0-5.507,2.466-5.507,5.507l0,0v56.181C235.646,145.591,238.107,148.066,241.153,148.083"/><path d="M328.348,148.083h63.073c3.052-0.017,5.521-2.486,5.538-5.538V86.364c-0.017-3.047-2.491-5.507-5.538-5.507h-63.073 c-3.042,0-5.507,2.466-5.507,5.507l0,0v56.181C322.841,145.591,325.302,148.066,328.348,148.083"/><path d="M328.348,67.227h63.073c3.047,0,5.521-2.461,5.538-5.507V5.507C396.942,2.46,394.468,0,391.421,0h-63.073 c-3.042,0-5.507,2.465-5.507,5.507l0,0v56.212C322.841,64.761,325.307,67.227,328.348,67.227"/><path d="M416.312,228.909h63.073c3.047,0,5.521-2.46,5.538-5.507V167.22c0-3.059-2.479-5.538-5.538-5.538l0,0h-63.073 c-3.041,0-5.507,2.466-5.507,5.507c0,0.01,0,0.021,0,0.031v56.181C410.805,226.443,413.271,228.909,416.312,228.909"/></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

4
dist/index.html vendored
View File

@@ -1,5 +1,5 @@
<!--
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
@@ -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.oReq.hasHeader=Http.hasHeader,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;var s=this;switch(e){case"loadend":s.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;e.open(s._method,s._url);for(key in s._events)e.addEventListener(key,s._events[key]);for(key in s._headers)e.setRequestHeader(key,s._headers[key]);e.withCredentials=!0,e.hasHeader=Http.hasHeader,e.send()}else t.bind(this)()});break;case"load":s.oReq.addEventListener("load",function(){401!==this.status&&t.bind(this)()});break;default:s.oReq.addEventListener(e,function(){t.bind(this)()})}},Http.prototype.setRequestHeader=function(e,t){this.oReq.setRequestHeader(e,t),this._headers[e]=t},Http.prototype.open=function(e,t){this._method=e,this._url=t,this.oReq.open(e,t)},Http.prototype.send=function(){this.oReq.send()},Http.hasHeader=function(e){return this.getAllResponseHeaders().split("\n").some(function(t){return t.match(new RegExp("^"+e+":"),"i")})};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.oReq.hasHeader=Http.hasHeader,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;var r=this;switch(e){case"loadend":r.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;e.open(r._method,r._url);for(key in r._events)e.addEventListener(key,r._events[key]);for(key in r._headers)e.setRequestHeader(key,r._headers[key]);e.withCredentials=!0,e.hasHeader=Http.hasHeader,e.send()}else t.bind(this)()});break;case"load":r.oReq.addEventListener("load",function(){401!==this.status&&t.bind(this)()});break;default:r.oReq.addEventListener(e,function(){t.bind(this)()})}},Http.prototype.setRequestHeader=function(e,t){this.oReq.setRequestHeader(e,t),this._headers[e]=t},Http.prototype.open=function(e,t){this._method=e,this._url=t,this.oReq.open(e,t)},Http.prototype.send=function(){this.oReq.send()},Http.hasHeader=function(e){return this.getAllResponseHeaders().split("\n").some(function(t){return t.match(new RegExp("^"+e+":"),"i")})};var registryUI={};registryUI.url=function(){return registryUI.getRegistryServer(0)},registryUI.getRegistryServer=function(e){try{var t=JSON.parse(localStorage.getItem("registryServer"));if(t instanceof Array)return isNaN(e)?t.map(function(e){return e.trim().replace(/\/*$/,"")}):t[e]}catch(e){}return isNaN(e)?[]:""},registryUI.addServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r==-1&&(t.push(e),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.changeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),t=[e].concat(t),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.removeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("add"),riot.mount("change"),riot.mount("remove"),riot.mount("menu"),riot.mount("app");

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

752
dist/scripts/vendor.js vendored

File diff suppressed because one or more lines are too long

4
dist/style.css vendored

File diff suppressed because one or more lines are too long

47
dist/vendor.css vendored

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
# Traefik example
Host the docker registry ui behind [traefik](http://traefik.io) with Docker Swarm mode.
## How to run
Open a terminal console and type
```bash
bash run-swarm.sh
```
## Authentication
The registry is protected via __Basic authentication__ but feel free to use wathever you like.
In this sample, credentials are: **admin / admin**.
To generate a new password for basic auth, run the command: `htpasswd -nb username password`.
## Contributors
Thank you [@onizet](https://github.com/onizet) for this example.

View File

View File

@@ -0,0 +1,33 @@
version: '3.1'
services:
registry:
image: registry:2.6.2
volumes:
- /opt/docker-registry:/var/lib/registry
environment:
- REGISTRY_HTTP_SECRET=my_registry_secret
- REGISTRY_STORAGE_DELETE_ENABLED=true
deploy:
placement:
constraints: [node.role == manager]
ui:
image: joxit/docker-registry-ui:static
environment:
- DELETE_IMAGES=true
- REGISTRY_TITLE=My Private Docker Registry
- REGISTRY_URL=http://docker-registry_registry:5000
depends_on: ['registry']
networks: ['proxy', 'default']
deploy:
labels:
traefik.backend: 'registry.mydomain.com'
traefik.frontend.rule: 'Host:registry.mydomain.com'
traefik.enable: 'true'
traefik.port: 80
traefik.docker.network: 'traefik-net'
traefik.frontend.auth.basic: 'admin:$apr1$XXrpwZre$ItZSXpoeB6bdPLCGT7eXG0'
traefik.frontend.passHostHeader: 'true'
networks:
proxy: {external: {name: 'traefik-net'}}

42
examples/traefik/run-swarm.sh Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env bash
if ! [[ `docker network ls | grep "traefik-net"` ]] &>/dev/null; then
echo "Setup traefik network"
docker network create --driver=overlay --attachable traefik-net
fi
if ! [[ `docker service ls | grep "traefik2"` ]] &>/dev/null; then
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# ensure acme.json wich will contains the letsencrypt certificates
touch "$dir"/acme.json && chmod 600 "$dir"/acme.json
docker service create --name traefik2 --detach=false \
--constraint node.role==manager \
--update-parallelism 1 --update-delay 10s \
--mode global \
--publish 80:80 \
--publish 443:443 \
--read-only \
--mount type=bind,source="$(pwd)"/acme.json,target=/etc/traefik/acme.json \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--network traefik-net \
traefik:1.7.4-alpine \
--entrypoints='Name:http Address::80 Redirect.EntryPoint:https' \
--entrypoints='Name:https Address::443 TLS' \
--defaultentrypoints=http,https \
--acme \
--acme.storage=/etc/traefik/acme.json \
--acme.entryPoint=https \
--acme.httpChallenge.entryPoint=http \
--acme.email=contact@mydomain.com \
--docker \
--docker.swarmMode \
--docker.domain=mydomain.com \
--docker.exposedByDefault=false \
--docker.watch \
--api
fi
docker stack deploy --compose-file docker-compose-swarm.yml docker-registry

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 and the docker registry will be accessible with <http://localhost>.
The simplest way is with `simple.yml` docker-compose file.
```sh
docker-compose -f simple.yml up -d
./populate.sh
```
You can add some credentials to access your registry wit `credentials.yml` docker-compose file.
Credentials for this example are login: `registry` and password: `ui` using bcrypt.
```sh
docker-compose -f credentials.yml up -d
./populate.sh
```

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

@@ -0,0 +1,22 @@
# Docker Registry Static as standalone example
You can set up the static user interface as standalone in several ways.
If you want to populate your registry, use `populate.sh` script.
The interface will be accessible with <http://localhost>.
Your docker registry will be accessible with <http://localhost:5000>.
The simplest way is with `simple.yml` docker-compose file.
```sh
docker-compose -f simple.yml up -d
./populate.sh
```
You can add some credentials to access your registry wit `credentials.yml` docker-compose file.
Credentials for this example are login: `registry` and password: `ui` using bcrypt.
```sh
docker-compose -f credentials.yml up -d
./populate.sh
```

View File

@@ -0,0 +1,20 @@
version: '2.0'
services:
registry:
image: registry:2.6.2
ports:
- 5000:5000
volumes:
- ./registry-data:/var/lib/registry
- ./registry-config/credentials.yml:/etc/docker/registry/config.yml
- ./registry-config/htpasswd:/etc/docker/registry/htpasswd
ui:
image: joxit/docker-registry-ui:static
ports:
- 80:80
environment:
- REGISTRY_TITLE=My Private Docker Registry
- URL=http://localhost:5000
depends_on:
- registry

View File

@@ -0,0 +1,17 @@
#!/bin/bash
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:static
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3.0
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3.0-static
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3-static
docker push localhost:5000/joxit/docker-registry-ui
docker tag registry:2.6.2 localhost:5000/registry:latest
docker tag registry:2.6.2 localhost:5000/registry:2.6.2
docker tag registry:2.6.2 localhost:5000/registry:2.6
docker tag registry:2.6.2 localhost:5000/registry:2.6.0
docker tag registry:2.6.2 localhost:5000/registry:2
docker push localhost:5000/registry

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,21 @@
version: 0.1
log:
fields:
service: registry
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
Access-Control-Allow-Origin: ['http://localhost']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
Access-Control-Allow-Headers: ['Authorization']
Access-Control-Max-Age: [1728000]
Access-Control-Allow-Credentials: [true]
Access-Control-Expose-Headers: ['Docker-Content-Digest']

View File

@@ -0,0 +1,19 @@
version: '2.0'
services:
registry:
image: registry:2.6.2
ports:
- 5000:5000
volumes:
- ./registry-data:/var/lib/registry
- ./registry-config/simple.yml:/etc/docker/registry/config.yml
ui:
image: joxit/docker-registry-ui:static
ports:
- 80:80
environment:
- REGISTRY_TITLE=My Private Docker Registry
- URL=http://localhost:5000
depends_on:
- registry

View File

@@ -1,23 +1,40 @@
'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 gIf = require('gulp-if');
const gulp = require('gulp');
const parallel = gulp.parallel;
const series = gulp.series;
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');
gulp.task('html', function() {
var htmlFilter = filter('**/*.html');
const allTags = ['src/tags/*.tag', 'src/tags/dialogs/*.tag'];
const allScripts = [
'src/scripts/http.js',
'src/scripts/script.js'
];
const staticTags = ['src/tags/*.tag'];
const staticScripts = [
'src/scripts/http.js',
'src/scripts/static.js'
];
function 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,69 +43,49 @@ 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);
});
function clean() {
return del(['dist']);
};
gulp.task('riot-tag', ['html'], function() {
return gulp.src('src/tags/*.tag')
.pipe(concat('tags.js'))
.pipe(riot())
.pipe(minifier({}, uglify))
function appStatic() {
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))
function app() {
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'
}))
function vendor() {
return gulp.src(['node_modules/riot/riot.min.js', 'node_modules/riot-route/dist/route.min.js', 'node_modules/riot-mui/build/js/riot-mui-min.js'])
.pipe(concat('vendor.js'))
.pipe(gulp.dest('dist/scripts'));
});
};
gulp.task('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'
}))
.pipe(gulp.dest('dist/scripts'));
});
gulp.task('styles', ['html'], function() {
function styles() {
return gulp.src(['src/*.css'])
.pipe(concat('style.css'))
.pipe(cleanCSS({
@@ -97,22 +94,28 @@ 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/'));
});
};
gulp.task('fonts', function() {
function fonts() {
return gulp.src('src/fonts/*')
.pipe(filter('**/*.{otf,eot,svg,ttf,woff,woff2}'))
.pipe(gulp.dest('dist/fonts'));
});
};
gulp.task('sources', ['riot-tag', 'riot-static-tag', 'scripts', 'scripts-static', 'styles'], function() {
gulp.start();
});
function svgs() {
return gulp.src(['src/images/*.svg'])
.pipe(htmlmin({
removeComments: false,
collapseWhitespace: true,
removeRedundantAttributes: true,
removeEmptyAttributes: true,
minifyJS: uglify
}))
.pipe(gulp.dest('dist/images/'));
};
gulp.task('build', ['clean'], function() {
gulp.start(['sources', 'fonts']);
});
exports.build = series(clean, html, parallel(fonts, styles, vendor, app, appStatic, svgs));

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.2",
"version": "0.6.1",
"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",
"gulp": "^3.9",
"del": "^3.0.0",
"gulp": "^4.0",
"gulp-clean-css": "^4.0.0",
"gulp-concat": "^2.6.0",
"gulp-filter": "^1.0.0",
"gulp-htmlmin": "^2.0.0",
"gulp-filter": "^5.1.0",
"gulp-htmlmin": "^5.0.1",
"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.5",
"gulp-uglify": "^3.0.1",
"gulp-useref": "^3.1.6",
"riot": "^3.13.2",
"riot-mui": "^0.1.1",
"riot-route": "^3.1.4",
"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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="svg2"
x="0px"
y="0px"
viewBox="-5.724 -43.601 730 600" fill="#777">
<path
d="m595.942,422.343 c -7.543,0.119 -13.562,6.331 -13.443,13.875 0.119,7.544 6.332,13.562 13.875,13.443 7.495,-0.118 13.494,-6.254 13.445,-13.75 -0.085,-7.578 -6.297,-13.652 -13.875,-13.568 0,0 -10e-4,0 0,0 m 0,24.398 c -5.975,0.272 -11.039,-4.352 -11.311,-10.326 -0.271,-5.976 4.352,-11.04 10.327,-11.312 5.975,-0.271 11.039,4.352 11.311,10.327 0.01,0.19 0.013,0.382 0.011,0.573 0.204,5.723 -4.27,10.527 -9.992,10.731 -0.115,0.005 -0.23,0.007 -0.346,0.007"/>
<path
d="m599.081,436.342 v -0.185 c 1.512,-0.292 2.65,-1.544 2.8,-3.076 0.057,-1.175 -0.432,-2.311 -1.323,-3.077 -1.445,-0.765 -3.076,-1.106 -4.707,-0.984 -1.743,-0.024 -3.484,0.12 -5.2,0.431 v 13.538 h 3.077 v -5.446 h 1.477 c 1.754,0 2.554,0.646 2.83,2.154 0.184,1.143 0.536,2.252 1.047,3.292 h 3.415 c -0.53,-1.062 -0.873,-2.207 -1.016,-3.385 -0.138,-1.473 -1.088,-2.744 -2.462,-3.292 m -3.723,-0.985 h -1.508 v -3.908 c 0.583,-0.069 1.172,-0.069 1.754,0 1.97,0 2.893,0.831 2.893,2.062 0,1.231 -1.415,2 -3.076,2"/>
<path
d="M707.494,193.557c-1.938-1.539-20.029-15.199-58.181-15.199c-10.074,0.044-20.127,0.908-30.061,2.584 c-7.384-50.612-49.228-75.288-51.104-76.395l-10.245-5.908l-6.738,9.723c-8.438,13.061-14.598,27.459-18.214,42.582 c-6.831,28.891-2.677,56.027,11.999,79.226c-17.722,9.876-46.151,12.307-51.904,12.522H22.367 c-12.294,0.017-22.27,9.952-22.337,22.245c-0.549,41.234,6.437,82.222,20.614,120.946c16.214,42.521,40.336,73.842,71.719,93.01 c35.167,21.537,92.302,33.844,157.067,33.844c29.258,0.092,58.461-2.556,87.226-7.907c39.986-7.342,78.463-21.318,113.839-41.352 c29.149-16.88,55.383-38.354,77.688-63.596c37.29-42.213,59.505-89.226,76.026-131.007c2.215,0,4.431,0,6.584,0 c40.828,0,65.935-16.338,79.78-30.029c9.201-8.732,16.384-19.369,21.045-31.167l2.923-8.553L707.494,193.557z"/>
<path
d="M65.995,228.909h63.073c3.042,0,5.507-2.466,5.507-5.507l0,0V167.22c0.017-3.042-2.435-5.521-5.476-5.538 c-0.01,0-0.021,0-0.031,0H65.995c-3.042,0-5.507,2.466-5.507,5.507c0,0.01,0,0.021,0,0.031v56.181 C60.488,226.443,62.953,228.909,65.995,228.909L65.995,228.909"/>
<path
d="M152.913,228.909h63.073c3.042,0,5.507-2.466,5.507-5.507l0,0V167.22c0.017-3.042-2.435-5.521-5.477-5.538 c-0.01,0-0.021,0-0.031,0h-63.073c-3.059,0-5.538,2.479-5.538,5.538v56.181C147.392,226.448,149.866,228.909,152.913,228.909"/>
<path
d="M241.153,228.909h63.073c3.042,0,5.507-2.466,5.507-5.507l0,0V167.22c0.017-3.042-2.435-5.521-5.477-5.538 c-0.01,0-0.021,0-0.031,0h-63.073c-3.042,0-5.507,2.466-5.507,5.507c0,0.01,0,0.021,0,0.031v56.181 C235.646,226.443,238.112,228.909,241.153,228.909L241.153,228.909"/>
<path
d="M328.348,228.909h63.073c3.047,0,5.521-2.46,5.538-5.507V167.22c0-3.059-2.479-5.538-5.538-5.538l0,0h-63.073 c-3.042,0-5.507,2.466-5.507,5.507c0,0.01,0,0.021,0,0.031v56.181C322.841,226.443,325.307,228.909,328.348,228.909L328.348,228.909"/>
<path
d="M152.913,148.083h63.073c3.046-0.017,5.507-2.492,5.507-5.538V86.364c0-3.042-2.466-5.507-5.507-5.507l0,0h-63.073 c-3.046,0-5.521,2.46-5.538,5.507v56.181C147.392,145.597,149.861,148.066,152.913,148.083"/>
<path
d="M241.153,148.083h63.073c3.046-0.017,5.507-2.492,5.507-5.538V86.364c0-3.042-2.466-5.507-5.507-5.507l0,0h-63.073 c-3.042,0-5.507,2.466-5.507,5.507l0,0v56.181C235.646,145.591,238.107,148.066,241.153,148.083"/>
<path
d="M328.348,148.083h63.073c3.052-0.017,5.521-2.486,5.538-5.538V86.364c-0.017-3.047-2.491-5.507-5.538-5.507h-63.073 c-3.042,0-5.507,2.466-5.507,5.507l0,0v56.181C322.841,145.591,325.302,148.066,328.348,148.083"/>
<path
d="M328.348,67.227h63.073c3.047,0,5.521-2.461,5.538-5.507V5.507C396.942,2.46,394.468,0,391.421,0h-63.073 c-3.042,0-5.507,2.465-5.507,5.507l0,0v56.212C322.841,64.761,325.307,67.227,328.348,67.227"/>
<path
d="M416.312,228.909h63.073c3.047,0,5.521-2.46,5.538-5.507V167.22c0-3.059-2.479-5.538-5.538-5.538l0,0h-63.073 c-3.041,0-5.507,2.466-5.507,5.507c0,0.01,0,0.021,0,0.031v56.181C410.805,226.443,413.271,228.909,416.312,228.909"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -1,5 +1,5 @@
<!--
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
@@ -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,28 @@
</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/tag-history-button.tag" type="riot/tag"></script>
<script src="tags/tag-history.tag" type="riot/tag"></script>
<script src="tags/tag-history-element.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/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/copy-to-clipboard.tag" type="riot/tag"></script>
<script src="tags/dialogs/add.tag" type="riot/tag"></script>
<script src="tags/dialogs/change.tag" type="riot/tag"></script>
<script src="tags/dialogs/remove.tag" type="riot/tag"></script>
<script src="tags/dialogs/menu.tag" type="riot/tag"></script>
<script src="tags/image-size.tag" type="riot/tag"></script>
<script src="tags/image-date.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

@@ -11,7 +11,9 @@
url(fonts/MaterialIcons-Regular.ttf) format('truetype');
}
.material-icons {
material-button .content i.material-icons,
material-button[rounded=true] .content i.material-icons,
i.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
@@ -36,4 +38,9 @@
/* Support for IE. */
font-feature-settings: 'liga';
}
}
material-button .content i.material-icons,
material-button[rounded=true] .content i.material-icons {
margin: auto;
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -17,19 +17,20 @@
function Http() {
this.oReq = new XMLHttpRequest();
this.oReq.hasHeader = Http.hasHeader;
this.oReq.getErrorMessage = Http.getErrorMessage;
this._events = {};
this._headers = {};
}
Http.prototype.addEventListener = function(e, f) {
this._events[e] = f;
var self = this;
const self = this;
switch (e) {
case 'loadend':
{
self.oReq.addEventListener('loadend', function() {
if (this.status == 401) {
var req = new XMLHttpRequest();
const req = new XMLHttpRequest();
req.open(self._method, self._url);
for (key in self._events) {
req.addEventListener(key, self._events[key]);
@@ -39,6 +40,7 @@ Http.prototype.addEventListener = function(e, f) {
}
req.withCredentials = true;
req.hasHeader = Http.hasHeader;
req.getErrorMessage = Http.getErrorMessage;
req.send();
} else {
f.bind(this)();
@@ -82,6 +84,17 @@ Http.prototype.send = function() {
Http.hasHeader = function(header) {
return this.getAllResponseHeaders().split('\n').some(function(h) {
return h.match(new RegExp('^' + header + ':'), 'i');
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

@@ -1,5 +1,5 @@
/*
* 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
@@ -15,12 +15,27 @@
* 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) {
const 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 {
var res = JSON.parse(localStorage.getItem('registryServer'));
const res = JSON.parse(localStorage.getItem('registryServer'));
if (res instanceof Array) {
return (!isNaN(i)) ? res[i] : res.map(function(url) {
return url.trim().replace(/\/*$/, '');
@@ -30,42 +45,73 @@ registryUI.getRegistryServer = function(i) {
return (!isNaN(i)) ? '' : [];
}
registryUI.addServer = function(url) {
var registryServer = registryUI.getRegistryServer();
const registryServer = registryUI.getRegistryServer();
url = url.trim().replace(/\/*$/, '');
var index = registryServer.indexOf(url);
const index = registryServer.indexOf(url);
if (index != -1) {
return;
}
registryServer.push(url);
if (!registryUI._url) {
registryUI.updateHistory(url);
}
localStorage.setItem('registryServer', JSON.stringify(registryServer));
}
};
registryUI.changeServer = function(url) {
var registryServer = registryUI.getRegistryServer();
url = url.trim().replace(/\/*$/, '');
var index = registryServer.indexOf(url);
const index = registryServer.indexOf(url);
if (index == -1) {
return;
}
registryServer.splice(index, 1);
registryServer = [url].concat(registryServer);
registryUI.updateHistory(url);
localStorage.setItem('registryServer', JSON.stringify(registryServer));
}
};
registryUI.removeServer = function(url) {
var registryServer = registryUI.getRegistryServer();
const registryServer = registryUI.getRegistryServer();
url = url.trim().replace(/\/*$/, '');
var index = registryServer.indexOf(url);
const index = registryServer.indexOf(url);
if (index == -1) {
return;
}
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 () {
const search = window.location.search;
if (registryUI.URL_QUERY_PARAM_REGEX.test(search)) {
const 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 = {};
registryUI.taghistory = {};
riot.mount('add');
riot.mount('change');
riot.mount('remove');
riot.mount('menu');
riot.mount('app');
window.addEventListener('DOMContentLoaded', function() {
riot.mount('*');
});

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -18,10 +18,14 @@ var registryUI = {}
registryUI.url = function() {
return '${URL}';
};
registryUI.name = function() {
return '${REGISTRY_TITLE}' || registryUI.url();
};
registryUI.isImageRemoveActivated = true;
registryUI.catalog = {};
registryUI.taglist = {};
registryUI.taghistory = {};
riot.mount('catalog');
riot.mount('taglist');
riot.mount('app');
window.addEventListener('DOMContentLoaded', function() {
riot.mount('*');
});

View File

@@ -19,27 +19,19 @@ html > body {
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif !important;
}
.mdl-mini-footer {
padding-top: 8px;
padding-bottom: 8px;
html, body {
margin: 0;
height: 100%;
}
.catalog, .taglist {
padding: 16px;
main {
margin-bottom: 100px;
}
.section-centerd {
margin: auto;
}
.mdl-data-table th {
font-size: 18px;
}
.mdl-data-table td {
font-size: 16px;
}
.full-table {
width: 100%;
border: none;
@@ -48,4 +40,361 @@ 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;
}
.material-card-title-action h2 .item-count {
font-size: 0.7em;
margin-left: 1em;
}
.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 material-button:hover,
material-card table tbody tr:hover {
background-color: #eee;
}
material-card material-button,
material-card table tbody tr {
transition-duration: .28s;
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
transition-property: background-color;
}
material-card table tbody tr {
position: relative;
height: 48px;
}
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;
}
tag-history-button button:hover,
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-button .content i.material-icons,
.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;
}
.show-tag-history {
width: 30px;
text-align: center;
}
.remove-tag {
padding: 12px 5px;
width: 30px;
text-align: center;
}
tag-history material-card {
min-height: auto;
}
tag-history-element i {
font-size: 20px;
padding: 0px;
}
tag-history-element.docker_version .headline .material-icons {
background-size: 24px auto;
background-image: url("images/docker-logo.svg");
background-repeat: no-repeat;
}
tag-history-element {
display: block;
padding: 20px;
min-width: 100px;
width: 420px;
float: left;
overflow-x: auto;
}
tag-history-element .headline p {
font-weight: bold;
line-height: 20px;
position: relative;
display: inline;
top: -4px;
}
tag-history-element.id div.value {
font-size: 12px;
}
tag-history-button button {
background: none;
border: none;
}
material-card material-button {
max-height: 30px;
max-width: 30px;
}
material-button:hover material-waves {
background: none;
}
material-card material-button {
background-color: inherit;
}

View File

@@ -1,65 +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/>.
-->
<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>
<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['add-server-input'].onkeyup = function (e) {
// if keyCode is Enter
if (e.keyCode == 13) {
registryUI.addTag.add();
}
};
});
registryUI.addTag.show = function () {
registryUI.addTag.dialog.showModal();
};
registryUI.addTag.add = function () {
if (registryUI.addTag.addServer.value && registryUI.addTag.addServer.value.length > 0) {
registryUI.addServer(registryUI.addTag.addServer.value);
}
registryUI.addTag.addServer.value = '';
rg.router.go('home');
registryUI.addTag.dialog.close();
};
registryUI.addTag.close = function () {
registryUI.addTag.addServer.value = '';
registryUI.addTag.dialog.close();
};
registryUI.addTag.update();
</script>
</add>

View File

@@ -1,43 +1,241 @@
<!--
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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.
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/>.
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/>.
-->
<app>
<catalog if="{!rg.router.current || rg.router.current.name == 'home'}"></catalog>
<taglist if="{rg.router.current.name == 'taglist'}"></taglist>
<script>
<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>
<tag-history if="{route.routeName == 'taghistory'}"></tag-history>
<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>
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;
</material-footer>
</footer>
<script>
registryUI.appTag = this;
route.base('#!');
route('', function() {
route.routeName = 'home';
if (registryUI.catalog.display) {
registryUI.catalog.loadend = false;
}
})
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.appTag.update();
});
route('/taghistory/image/*/tag/*', function(image, tag) {
route.routeName = 'taghistory';
registryUI.taghistory.image = image;
registryUI.taghistory.tag = tag;
if (registryUI.taghistory.display) {
registryUI.taghistory.loadend = false;
}
registryUI.appTag.update();
});
registryUI.home = function() {
if (route.routeName == 'home') {
registryUI.catalog.display;
} else {
route('');
}
};
registryUI.taghistory.go = function(image, tag) {
route('/taghistory/image/' + image + '/tag/' + tag);
};
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() {
const 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();
});
this.on('get-date', function() {
if (this.date !== undefined) {
return this.trigger('date', this.date);
}
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) {
const tag1 = e1.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
const tag2 = e2.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
for (var i = 0; i < tag1.length && i < tag2.length; i++) {
const compare = tag1[i].localeCompare(tag2[i]);
if (registryUI.isDigit(tag1[i].charAt(0)) && registryUI.isDigit(tag2[i].charAt(0))) {
const 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;
const oReq = new Http();
const self = this;
oReq.addEventListener('loadend', function() {
if (this.status == 200 || this.status == 202) {
const response = JSON.parse(this.responseText);
self.size = response.layers.reduce(function(acc, e) {
return acc + e.size;
}, 0);
self.sha256 = response.config.digest;
self.layers = response.layers;
self.trigger('size', self.size);
self.trigger('sha256', self.sha256);
self.getBlobs(response.config.digest)
} 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();
};
registryUI.DockerImage.prototype.getBlobs = function(blob) {
const oReq = new Http();
const self = this;
oReq.addEventListener('loadend', function() {
if (this.status == 200 || this.status == 202) {
const response = JSON.parse(this.responseText);
self.creationDate = new Date(response.created);
self.blobs = response;
self.blobs.history.filter(function(e) {
return !e.empty_layer;
}).forEach(function(e, i) {
e.size = self.layers[i].size;
e.id = self.layers[i].digest.replace('sha256:', '');
});
self.blobs.id = blob.replace('sha256:', '');
self.trigger('creation-date', self.creationDate);
self.trigger('blobs', self.blobs);
} else if (this.status == 404) {
registryUI.errorSnackbar('Blobs for ' + self.name + ':' + self.tag + ' not found');
} else {
registryUI.snackbar(this.responseText);
}
});
oReq.open('GET', registryUI.url() + '/v2/' + self.name + '/blobs/' + blob);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
};
registryUI.bytesToSize = function (bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == undefined || isNaN(bytes)) {
return '?';
} else if (bytes == 0) {
return '0 Byte';
}
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.ceil(bytes / Math.pow(1024, i)) + ' ' + sizes[i];
};
registryUI.taglist.go = function(image) {
route('taglist/' + image);
};
route.start(true);
</script>
</app>
</app>

View File

@@ -1,90 +1,69 @@
<!--
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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.
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/>.
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/>.
-->
<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() }
<div class="item-count">{ registryUI.catalog.repositories.length } images</div>
</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.taglist.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 () {
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.display = function() {
registryUI.catalog.repositories = [];
const oReq = new Http();
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');
oReq.addEventListener('error', function() {
registryUI.snackbar(this.getErrorMessage(), true);
registryUI.catalog.repositories = [];
});
oReq.addEventListener('loadend', function () {
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]
});
};
registryUI.catalog.display();
</script>
<!-- End of tag -->
</catalog>
</catalog>

View File

@@ -1,66 +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/>.
-->
<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">
<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>
</dialog>
<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();
}
};
});
registryUI.changeTag.show = function () {
registryUI.changeTag.update();
registryUI.changeTag.serverList.value = registryUI.url();;
registryUI.changeTag.dialog.showModal();
};
registryUI.changeTag.change = function () {
if (registryUI.changeTag.serverList.value && registryUI.changeTag.serverList.value.length > 0) {
registryUI.changeServer(registryUI.changeTag.serverList.value);
}
registryUI.changeTag.serverList.value = '';
rg.router.go('home');
registryUI.changeTag.dialog.close();
};
registryUI.changeTag.close = function () {
registryUI.changeTag.dialog.close();
};
</script>
</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 }">
<material-button waves-center="true" rounded="true" waves-color="#ddd" onclick="{ this.copy }" title="Copy pull command.">
<i class="material-icons">content_copy</i>
</material-button>
<script type="text/javascript">
this.dockerCmd = 'docker pull ' + registryUI.cleanName() + '/' + opts.image.name + ':' + opts.image.tag;
this.copy = function () {
const 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>

59
src/tags/dialogs/add.tag Normal file
View File

@@ -0,0 +1,59 @@
<!--
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/>.
-->
<add>
<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 || {};
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 : '';
}
});
registryUI.addTag.onkeyup = function (e) {
// if keyCode is Enter
if (e.keyCode == 13) {
registryUI.addTag.add();
}
};
registryUI.addTag.show = function () {
registryUI.addTag.dialog.open();
};
registryUI.addTag.add = function () {
if (registryUI.addTag.dialog.getAddServer().length > 0) {
registryUI.addServer(registryUI.addTag.dialog.getAddServer());
}
registryUI.home();
registryUI.addTag.close();
};
registryUI.addTag.close = function () {
registryUI.addTag.dialog.tags['material-input'].value = '';
registryUI.addTag.dialog.close();
};
</script>
</add>

View File

@@ -0,0 +1,61 @@
<!--
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/>.
-->
<change>
<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="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>
</material-popup>
<script type="text/javascript">
registryUI.changeTag = registryUI.changeTag || {};
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.dialog.open();
};
registryUI.changeTag.change = function () {
if (registryUI.changeTag.dialog.getServerUrl().length > 0) {
registryUI.changeServer(registryUI.changeTag.dialog.getServerUrl());
}
registryUI.home();
registryUI.changeTag.dialog.close();
};
registryUI.changeTag.close = function () {
registryUI.changeTag.dialog.close();
};
</script>
</change>

45
src/tags/dialogs/menu.tag Normal file
View File

@@ -0,0 +1,45 @@
<!--
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/>.
-->
<menu>
<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.one('mount', function(args) {
const 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();
};
});
</script>
</menu>

View File

@@ -0,0 +1,61 @@
<!--
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/>.
-->
<remove>
<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.close();
};
registryUI.removeTag.close = function() {
registryUI.removeTag.dialog.close();
registryUI.removeTag.update();
};
registryUI.removeTag.show = function() {
registryUI.removeTag.dialog.open();
};
this.one('mount', function() {
registryUI.removeTag.dialog = this.tags['material-popup'];
});
</script>
</remove>

43
src/tags/image-date.tag Normal file
View File

@@ -0,0 +1,43 @@
<!--
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-date>
<div title="Creation date { this.localDate }">{ this.dateFormat(this.date) } ago</div>
<script type="text/javascript">
const self = this;
this.dateFormat = function(date) {
if (date === undefined) {
return '';
}
const labels = ['a second', 'seconds', 'a minute', 'minutes', 'an hour', 'hours', 'a day', 'days', 'a month', 'months', 'a year', 'years'];
const maxSeconds = [1, 60, 3600, 86400, 2592000, 31104000, Infinity];
const diff = (new Date() - date) / 1000;
for (var i = 0; i < maxSeconds.length - 1; i++) {
if (maxSeconds[i] * 2 >= diff) {
return labels[i * 2];
} else if (maxSeconds[i + 1] > diff) {
return Math.floor(diff / maxSeconds[i]) + ' ' + labels[i * 2 + 1];
}
}
};
opts.image.on('creation-date', function(date) {
self.date = date;
self.localDate = date.toLocaleString()
self.update();
});
opts.image.trigger('get-date');
</script>
</image-date>

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

@@ -0,0 +1,27 @@
<!--
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.">{ registryUI.bytesToSize(this.size) }</div>
<script type="text/javascript">
const self = this;
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">
const 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

@@ -1,37 +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/>.
-->
<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>
<script type="text/javascript">
registryUI.menuTag = registryUI.menuTag || {};
registryUI.menuTag.update = this.update;
this.on('updated', function () {
componentHandler.upgradeElements(this['card-menu']);
});
registryUI.menuTag.update();
</script>
</menu>

View File

@@ -1,63 +1,65 @@
<!--
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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.
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/>.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<remove-image>
<a href="#" onclick="registryUI.removeImage.remove('{ opts.name }', '{ opts.tag }')">
<i class="material-icons mdl-list__item-icon">delete</i>
</a>
<material-button waves-center="true" rounded="true" waves-color="#ddd" title="This will delete the image.">
<i class="material-icons">delete</i>
</material-button>
<script type="text/javascript">
registryUI.removeImage = registryUI.removeImage || {}
registryUI.removeImage.update = this.update;
registryUI.removeImage.remove = function (name, tag) {
var oReq = new Http();
oReq.addEventListener('loadend', function () {
registryUI.taglist.refresh();
if (this.status == 200) {
if (!this.hasHeader('Docker-Content-Digest')) {
registryUI.taglist.createSnackbar('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('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');
} else if (this.status == 404) {
registryUI.taglist.createSnackbar('Digest not found');
} else {
registryUI.taglist.createSnackbar(this.responseText);
const self = this;
this.on('mount', function() {
this.tags['material-button'].root.onclick = function() {
const name = self.opts.image.name;
const tag = self.opts.image.tag;
const oReq = new Http();
oReq.addEventListener('loadend', function() {
registryUI.taglist.go(name);
if (this.status == 200) {
if (!this.hasHeader('Docker-Content-Digest')) {
registryUI.errorSnackbar('You need to add Access-Control-Expose-Headers: [\'Docker-Content-Digest\'] in your server configuration.');
return;
}
});
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\'].');
});
oReq.send();
} else if (this.status == 404) {
registryUI.taglist.createSnackbar('Manifest for' + name + ':' + tag + 'not found');
} else {
registryUI.taglist.createSnackbar(this.responseText);
}
});
oReq.open('HEAD', registryUI.url() + '/v2/' + name + '/manifests/' + tag);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
};
const digest = this.getResponseHeader('Docker-Content-Digest');
const oReq = new Http();
oReq.addEventListener('loadend', function() {
if (this.status == 200 || this.status == 202) {
registryUI.taglist.display()
registryUI.snackbar('Deleting ' + name + ':' + tag + ' image. Run `registry garbage-collect config.yml` on your registry');
} else if (this.status == 404) {
registryUI.errorSnackbar('Digest not found');
} else {
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.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.errorSnackbar('Manifest for ' + name + ':' + tag + ' not found');
} else {
registryUI.snackbar(this.responseText);
}
});
oReq.open('HEAD', registryUI.url() + '/v2/' + name + '/manifests/' + tag);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
};
});
</script>
</remove-image>

View File

@@ -1,64 +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/>.
-->
<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>
<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 = function () {
registryUI.removeTag.dialog.close();
registryUI.removeTag.update();
};
registryUI.removeTag.show = function () {
registryUI.removeTag.update();
registryUI.removeTag.dialog.showModal();
};
this.on('updated', function () {
registryUI.removeTag.dialog = this['remove-server-dialog'];
if (!registryUI.removeTag.dialog.showModal) {
dialogPolyfill.registerDialog(registryUI.removeTag.dialog);
}
});
</script>
</remove>

View File

@@ -0,0 +1,31 @@
<!--
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/>.
-->
<tag-history-button>
<material-button ref="button" title="This will show the history of given tag" waves-center="true" rounded="true" waves-color="#ddd">
<i class="material-icons">history</i>
</material-button>
<script>
this.on('mount', function() {
const self = this;
this.refs.button.root.onclick = function() {
registryUI.taghistory._image = self.opts.image;
registryUI.taghistory.go(self.opts.image.name, self.opts.image.tag);
};
});
this.update()
</script>
</tag-history-button>

View File

@@ -0,0 +1,64 @@
<!--
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/>.
-->
<tag-history-element class="{entry.key}">
<div class="headline"><i class="material-icons">{ this.getIcon(entry.key) }</i>
<p>{ entry.key.replace('_', ' ') }</p>
</div>
<div class="value" if={!(entry.value instanceof Array)}> { entry.value }</div>
<div class="value" each={ e in entry.value } if={entry.value instanceof Array}> { e }</div>
<script type="text/javascript">
this.getIcon = function(attribute) {
switch (attribute) {
case 'architecture':
return 'memory';
case 'created':
return 'event';
case 'docker_version':
return '';
case 'os':
return 'developer_board';
case 'Cmd':
return 'launch';
case 'Entrypoint':
return 'input';
case 'Env':
return 'notes';
case 'Labels':
return 'label';
case 'User':
return 'face';
case 'Volumes':
return 'storage';
case 'WorkingDir':
return 'home';
case 'author':
return 'account_circle';
case 'id':
case 'digest':
return 'settings_ethernet';
case 'created_by':
return 'build';
case 'size':
return 'get_app';
case 'ExposedPorts':
return 'router';
default:
''
}
}
</script>
</tag-history-element>

137
src/tags/tag-history.tag Normal file
View File

@@ -0,0 +1,137 @@
<!--
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/>.
-->
<tag-history>
<material-card ref="tag-history-tag" class="tag-history">
<div class="material-card-title-action">
<material-button waves-center="true" rounded="true" waves-color="#ddd">
<i class="material-icons">arrow_back</i>
</material-button>
<h2>
History of { registryUI.taghistory.image }:{ registryUI.taghistory.tag } <i class="material-icons">history</i>
</h2>
</div>
</material-card>
<div hide="{ registryUI.taghistory.loadend }" class="spinner-wrapper">
<material-spinner/>
</div>
<material-card each="{ guiElement in this.elements }" class="tag-history-element">
<tag-history-element each="{ entry in guiElement }" if="{ entry.value && entry.value.length > 0}"/>
</material-card>
<script type="text/javascript">
const self = this;
const eltIdx = function(e) {
switch (e) {
case 'id': return 1;
case 'created': return 2;
case 'created_by': return 3;
case 'size': return 4;
case 'os': return 5;
case 'architecture': return 6;
case 'linux': return 7;
case 'docker_version': return 8;
default: return 10;
}
};
const eltSort = function(e1, e2) {
return eltIdx(e1.key) - eltIdx(e2.key);
};
const modifySpecificAttributeTypes = function(attribute, value) {
switch (attribute) {
case 'created':
return new Date(value).toLocaleString();
case 'created_by':
const cmd = value.match(/\/bin\/sh *-c *#\(nop\) *([A-Z]+)/);
return (cmd && cmd [1]) || 'RUN'
case 'size':
return registryUI.bytesToSize(value);
case 'Entrypoint':
case 'Cmd':
return (value || []).join(' ');
case 'Labels':
return Object.keys(value || {}).map(function(elt) {
return value[elt] ? elt + '=' + value[elt] : '';
});
case 'Volumes':
case 'ExposedPorts':
return Object.keys(value);
}
return value || '';
};
const getConfig = function(blobs) {
const res = ['architecture', 'User', 'created', 'docker_version', 'os', 'Cmd', 'Entrypoint', 'Env', 'Labels', 'User', 'Volumes', 'WorkingDir', 'author', 'id', 'ExposedPorts'].reduce(function(acc, e) {
const value = blobs[e] || blobs.config[e];
if (value) {
acc[e] = value;
}
return acc;
}, {});
if (!res.author && (res.Labels && res.Labels.maintainer)) {
res.author = blobs.config.Labels.maintainer;
delete res.Labels.maintainer;
}
return res;
};
const processBlobs = function(blobs) {
function exec(elt) {
const guiElements = [];
for (const attribute in elt) {
if (elt.hasOwnProperty(attribute) && attribute != 'empty_layer') {
const value = elt[attribute];
const guiElement = {
"key": attribute,
"value": modifySpecificAttributeTypes(attribute, value)
};
guiElements.push(guiElement);
}
}
return guiElements.sort(eltSort);
}
self.elements.push(exec(getConfig(blobs)));
blobs.history.reverse().forEach(function(elt) { self.elements.push(exec(elt)) });
registryUI.taghistory.loadend = true;
self.update();
};
registryUI.taghistory.display = function() {
self.elements = []
const blobs = registryUI.taghistory._image && registryUI.taghistory._image.blobs;
if (blobs) {
return processBlobs(blobs)
}
const image = new registryUI.DockerImage(registryUI.taghistory.image, registryUI.taghistory.tag);
image.fillInfo()
image.on('blobs', processBlobs);
};
this.on('mount', function() {
self.refs['tag-history-tag'].tags['material-button'].root.onclick = function() {
registryUI.taglist.go(registryUI.taghistory.image);
};
});
registryUI.taghistory.display();
self.update();
</script>
</tag-history>

View File

@@ -1,122 +1,121 @@
<!--
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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.
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/>.
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/>.
-->
<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">
<material-button waves-center="true" rounded="true" waves-color="#ddd" onclick="registryUI.home();">
<i class="material-icons">arrow_back</i>
</material-button>
<h2>
Tags of { registryUI.name() + '/' + registryUI.taglist.name }
<div class="item-count">{ registryUI.taglist.tags.length } tags</div>
</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>Creation date</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 class="show-tag-history">History</th>
<th class="remove-tag" 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-date image="{ image }"/>
</td>
<td>
<image-size image="{ image }"/>
</td>
<td>
<image-tag image="{ image }"/>
</td>
<td class="show-tag-history">
<tag-history-button 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
: '');
var oReq = new Http();
registryUI.taglist.name = name;
registryUI.taglist.display = function() {
registryUI.taglist.tags = [];
if (route.routeName == 'taglist') {
const oReq = new Http();
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 () {
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');
oReq.addEventListener('error', function() {
registryUI.snackbar(this.getErrorMessage(), true);
registryUI.taglist.tags = [];
});
oReq.addEventListener('loadend', function () {
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 () {
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);
};
</script>
<!-- End of tag -->
</taglist>

38
static.dockerfile Normal file
View File

@@ -0,0 +1,38 @@
# 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 node:10-alpine AS builder
WORKDIR /usr/app
COPY package.json .
RUN yarn install
COPY . .
RUN yarn build
FROM nginx:alpine
LABEL maintainer="Jones MAGLOIRE @Joxit"
WORKDIR /usr/share/nginx/html/
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /usr/app/dist/ /usr/share/nginx/html/
COPY --from=builder /usr/app/dist/scripts/docker-registry-ui-static.js /usr/share/nginx/html/scripts/docker-registry-ui.js
COPY bin/entrypoint /bin
ENTRYPOINT entrypoint