mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2026-02-17 21:19:51 +00:00
Compare commits
124 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f014c1d8f | ||
|
|
e6d9f11b83 | ||
|
|
a36809408c | ||
|
|
7e2e4b6010 | ||
|
|
605e8a8d8e | ||
|
|
ac5a70c9df | ||
|
|
9b120bb6d5 | ||
|
|
7446452b77 | ||
|
|
d361068529 | ||
|
|
b03f00ebe8 | ||
|
|
d0b7e7ddeb | ||
|
|
7366e709a4 | ||
|
|
89e2782751 | ||
|
|
3911310d89 | ||
|
|
d599c1c202 | ||
|
|
1e185b4034 | ||
|
|
03e4d6b8c5 | ||
|
|
fe2fcc1104 | ||
|
|
581975b99e | ||
|
|
ed1e928bf3 | ||
|
|
ffd0a7c628 | ||
|
|
cb50dd42d8 | ||
|
|
5aaedfb0aa | ||
|
|
7cb06d57ee | ||
|
|
a6b6c1531b | ||
|
|
e89a0112ae | ||
|
|
4aa016090c | ||
|
|
7163150cf5 | ||
|
|
c176a082d9 | ||
|
|
67ad46a851 | ||
|
|
06a11a706b | ||
|
|
04259ab43d | ||
|
|
896031e894 | ||
|
|
8643fb16ff | ||
|
|
becf8bf887 | ||
|
|
472b485455 | ||
|
|
637f7635dc | ||
|
|
ae78b2d355 | ||
|
|
249d021152 | ||
|
|
e3437daefe | ||
|
|
246369fdec | ||
|
|
b94a65d79b | ||
|
|
bf975cd29b | ||
|
|
292336c1b6 | ||
|
|
6d849a9e95 | ||
|
|
86f78f7604 | ||
|
|
43af3ffdcf | ||
|
|
f364564d0e | ||
|
|
39cf28e562 | ||
|
|
66fe613b6f | ||
|
|
cf883cbfc7 | ||
|
|
5bb7dfce7d | ||
|
|
6d798ca75f | ||
|
|
f6bc4df11f | ||
|
|
8ab6ecbf19 | ||
|
|
c857bd8db6 | ||
|
|
fe5e962488 | ||
|
|
96a926652b | ||
|
|
3f860cd0b5 | ||
|
|
3b1b6f2e72 | ||
|
|
20861bbb0d | ||
|
|
09b77201be | ||
|
|
2f5e0dd307 | ||
|
|
08c5eaee7e | ||
|
|
4cb79a670f | ||
|
|
5173110883 | ||
|
|
d3e93f7064 | ||
|
|
6221958c78 | ||
|
|
05c2cf2425 | ||
|
|
82efd33c14 | ||
|
|
13936aadb1 | ||
|
|
6bff056086 | ||
|
|
b28fe68dcd | ||
|
|
d6523a4205 | ||
|
|
3b5f5201a6 | ||
|
|
cc624754b5 | ||
|
|
b78fd358d7 | ||
|
|
3430878e7d | ||
|
|
354d3159bd | ||
|
|
86c46deb7e | ||
|
|
ace12d0ac8 | ||
|
|
4d1f47e808 | ||
|
|
642afd695d | ||
|
|
b5e07ea14f | ||
|
|
ad01f3ece9 | ||
|
|
4cae0dec8a | ||
|
|
d5451f37c6 | ||
|
|
7ebab2f71c | ||
|
|
480cde5750 | ||
|
|
d7af9f1822 | ||
|
|
6f5a5ff756 | ||
|
|
b98b0c383d | ||
|
|
23a57e6c34 | ||
|
|
e41d6cfb85 | ||
|
|
c563f7dd4c | ||
|
|
bf3e3c9fa8 | ||
|
|
325a2417f4 | ||
|
|
adab8b03de | ||
|
|
89c532edbd | ||
|
|
b9effee691 | ||
|
|
05090d121d | ||
|
|
c8d98f68d1 | ||
|
|
a8bede51db | ||
|
|
cce858cbd9 | ||
|
|
215733c6e4 | ||
|
|
93a8d4a188 | ||
|
|
ab0571eaa1 | ||
|
|
dc929951cf | ||
|
|
91497a06c3 | ||
|
|
4d1db1f884 | ||
|
|
a59e759c9c | ||
|
|
46b80e73f2 | ||
|
|
bdc0d34a87 | ||
|
|
d4b99b2cb2 | ||
|
|
c1f9d02afb | ||
|
|
d404ead587 | ||
|
|
5569f1f0bf | ||
|
|
9d48643957 | ||
|
|
961342d34c | ||
|
|
02c95fee44 | ||
|
|
ca5fb09fc6 | ||
|
|
83e2befd5d | ||
|
|
06e4d62543 | ||
|
|
5cf136541f |
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
||||
*
|
||||
!dist
|
||||
!bin
|
||||
!nginx
|
||||
!src
|
||||
!package.json
|
||||
!gulpfile.js
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,5 @@
|
||||
.project
|
||||
node_modules
|
||||
package-lock.json
|
||||
registry-data
|
||||
.idea
|
||||
18
Dockerfile
18
Dockerfile
@@ -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/
|
||||
@@ -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
1
Dockerfile.static
Symbolic link
@@ -0,0 +1 @@
|
||||
static.dockerfile
|
||||
77
README.md
77
README.md
@@ -5,21 +5,32 @@
|
||||
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/)
|
||||
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- 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
|
||||
- Image aggregation (see #56)
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -48,31 +59,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 +105,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 +148,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 +156,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
26
arm32v7-static.dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2016-2018 Jones Magloire @Joxit
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
FROM 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
21
arm32v7.dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2016-2018 Jones Magloire @Joxit
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
FROM arm32v7/nginx
|
||||
|
||||
LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
COPY dist/ /usr/share/nginx/html/
|
||||
26
arm64v8-static.dockerfile
Normal file
26
arm64v8-static.dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2016-2018 Jones Magloire @Joxit
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
FROM arm64v8/nginx
|
||||
|
||||
LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY dist/ /usr/share/nginx/html/
|
||||
COPY dist/scripts/docker-registry-ui-static.js /usr/share/nginx/html/scripts/docker-registry-ui.js
|
||||
COPY bin/entrypoint /bin
|
||||
|
||||
ENTRYPOINT entrypoint
|
||||
21
arm64v8.dockerfile
Normal file
21
arm64v8.dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2016-2018 Jones Magloire @Joxit
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
FROM arm64v8/nginx
|
||||
|
||||
LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
COPY dist/ /usr/share/nginx/html/
|
||||
@@ -1,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
26
debian-static.dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2016-2018 Jones Magloire @Joxit
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
FROM 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
21
debian.dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2016-2018 Jones Magloire @Joxit
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
FROM nginx:latest
|
||||
|
||||
LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
COPY dist/ /usr/share/nginx/html/
|
||||
@@ -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 & 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;
|
||||
|
||||
BIN
dist/fonts/MaterialIcons-Regular.eot
vendored
BIN
dist/fonts/MaterialIcons-Regular.eot
vendored
Binary file not shown.
BIN
dist/fonts/MaterialIcons-Regular.ttf
vendored
BIN
dist/fonts/MaterialIcons-Regular.ttf
vendored
Binary file not shown.
BIN
dist/fonts/MaterialIcons-Regular.woff
vendored
BIN
dist/fonts/MaterialIcons-Regular.woff
vendored
Binary file not shown.
BIN
dist/fonts/MaterialIcons-Regular.woff2
vendored
BIN
dist/fonts/MaterialIcons-Regular.woff2
vendored
Binary file not shown.
1
dist/images/docker-logo.svg
vendored
Normal file
1
dist/images/docker-logo.svg
vendored
Normal 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
4
dist/index.html
vendored
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
Copyright (C) 2016 Jones Magloire @Joxit
|
||||
Copyright (C) 2016-2019 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&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 & 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&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>
|
||||
18
dist/scripts/docker-registry-ui-static.js
vendored
Normal file
18
dist/scripts/docker-registry-ui-static.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
dist/scripts/docker-registry-ui.js
vendored
Normal file
18
dist/scripts/docker-registry-ui.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
dist/scripts/script-static.js
vendored
18
dist/scripts/script-static.js
vendored
@@ -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");
|
||||
18
dist/scripts/script.js
vendored
18
dist/scripts/script.js
vendored
@@ -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");
|
||||
18
dist/scripts/tags-static.js
vendored
18
dist/scripts/tags-static.js
vendored
File diff suppressed because one or more lines are too long
18
dist/scripts/tags.js
vendored
18
dist/scripts/tags.js
vendored
File diff suppressed because one or more lines are too long
752
dist/scripts/vendor.js
vendored
752
dist/scripts/vendor.js
vendored
File diff suppressed because one or more lines are too long
4
dist/style.css
vendored
4
dist/style.css
vendored
File diff suppressed because one or more lines are too long
47
dist/vendor.css
vendored
47
dist/vendor.css
vendored
File diff suppressed because one or more lines are too long
BIN
docker-registry-ui.gif
Normal file
BIN
docker-registry-ui.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 764 KiB |
22
examples/traefik/README.md
Normal file
22
examples/traefik/README.md
Normal 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.
|
||||
0
examples/traefik/acme.json
Normal file
0
examples/traefik/acme.json
Normal file
33
examples/traefik/docker-compose-swarm.yml
Normal file
33
examples/traefik/docker-compose-swarm.yml
Normal 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
42
examples/traefik/run-swarm.sh
Executable 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
|
||||
21
examples/ui-as-proxy/README.md
Normal file
21
examples/ui-as-proxy/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Docker Registry Static as proxy example
|
||||
|
||||
You can set up the static user interface as proxy in several ways.
|
||||
|
||||
If you want to populate your registry, use `populate.sh` script.
|
||||
The interface and the docker registry will be accessible with <http://localhost>.
|
||||
|
||||
The simplest way is with `simple.yml` docker-compose file.
|
||||
|
||||
```sh
|
||||
docker-compose -f simple.yml up -d
|
||||
./populate.sh
|
||||
```
|
||||
|
||||
You can add some credentials to access your registry wit `credentials.yml` docker-compose file.
|
||||
Credentials for this example are login: `registry` and password: `ui` using bcrypt.
|
||||
|
||||
```sh
|
||||
docker-compose -f credentials.yml up -d
|
||||
./populate.sh
|
||||
```
|
||||
25
examples/ui-as-proxy/credentials.yml
Normal file
25
examples/ui-as-proxy/credentials.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
- ./registry-config/credentials.yml:/etc/docker/registry/config.yml
|
||||
- ./registry-config/htpasswd:/etc/docker/registry/htpasswd
|
||||
networks:
|
||||
- registry-ui-net
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
- REGISTRY_URL=http://registry:5000
|
||||
depends_on:
|
||||
- registry
|
||||
networks:
|
||||
- registry-ui-net
|
||||
|
||||
networks:
|
||||
registry-ui-net:
|
||||
17
examples/ui-as-proxy/populate.sh
Normal file
17
examples/ui-as-proxy/populate.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:static
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:0.3
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:0.3.0
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:0.3.0-static
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:0.3-static
|
||||
|
||||
docker push localhost/joxit/docker-registry-ui
|
||||
|
||||
docker tag registry:2.6.2 localhost/registry:latest
|
||||
docker tag registry:2.6.2 localhost/registry:2.6.2
|
||||
docker tag registry:2.6.2 localhost/registry:2.6
|
||||
docker tag registry:2.6.2 localhost/registry:2.6.0
|
||||
docker tag registry:2.6.2 localhost/registry:2
|
||||
|
||||
docker push localhost/registry
|
||||
25
examples/ui-as-proxy/registry-config/credentials.yml
Normal file
25
examples/ui-as-proxy/registry-config/credentials.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: 0.1
|
||||
log:
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
delete:
|
||||
enabled: true
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
http:
|
||||
addr: :5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
Access-Control-Allow-Origin: ['http://localhost']
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
|
||||
Access-Control-Allow-Headers: ['Authorization']
|
||||
Access-Control-Max-Age: [1728000]
|
||||
Access-Control-Allow-Credentials: [true]
|
||||
Access-Control-Expose-Headers: ['Docker-Content-Digest']
|
||||
auth:
|
||||
htpasswd:
|
||||
realm: basic-realm
|
||||
path: /etc/docker/registry/htpasswd
|
||||
1
examples/ui-as-proxy/registry-config/htpasswd
Normal file
1
examples/ui-as-proxy/registry-config/htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
registry:$2y$11$1bmuJLK8HrQl5ACS/WeqRuJLUArUZfUcP2R23asmozEpfN76.pCHy
|
||||
23
examples/ui-as-proxy/simple.yml
Normal file
23
examples/ui-as-proxy/simple.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
networks:
|
||||
- docker-registry-ui
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
- REGISTRY_URL=http://registry:5000
|
||||
depends_on:
|
||||
- registry
|
||||
networks:
|
||||
- docker-registry-ui
|
||||
|
||||
networks:
|
||||
registry-ui-net:
|
||||
22
examples/ui-as-standalone/README.md
Normal file
22
examples/ui-as-standalone/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Docker Registry Static as standalone example
|
||||
|
||||
You can set up the static user interface as standalone in several ways.
|
||||
|
||||
If you want to populate your registry, use `populate.sh` script.
|
||||
The interface will be accessible with <http://localhost>.
|
||||
Your docker registry will be accessible with <http://localhost:5000>.
|
||||
|
||||
The simplest way is with `simple.yml` docker-compose file.
|
||||
|
||||
```sh
|
||||
docker-compose -f simple.yml up -d
|
||||
./populate.sh
|
||||
```
|
||||
|
||||
You can add some credentials to access your registry wit `credentials.yml` docker-compose file.
|
||||
Credentials for this example are login: `registry` and password: `ui` using bcrypt.
|
||||
|
||||
```sh
|
||||
docker-compose -f credentials.yml up -d
|
||||
./populate.sh
|
||||
```
|
||||
20
examples/ui-as-standalone/credentials.yml
Normal file
20
examples/ui-as-standalone/credentials.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
ports:
|
||||
- 5000:5000
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
- ./registry-config/credentials.yml:/etc/docker/registry/config.yml
|
||||
- ./registry-config/htpasswd:/etc/docker/registry/htpasswd
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
- URL=http://localhost:5000
|
||||
depends_on:
|
||||
- registry
|
||||
17
examples/ui-as-standalone/populate.sh
Executable file
17
examples/ui-as-standalone/populate.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:static
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3.0
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3.0-static
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3-static
|
||||
|
||||
docker push localhost:5000/joxit/docker-registry-ui
|
||||
|
||||
docker tag registry:2.6.2 localhost:5000/registry:latest
|
||||
docker tag registry:2.6.2 localhost:5000/registry:2.6.2
|
||||
docker tag registry:2.6.2 localhost:5000/registry:2.6
|
||||
docker tag registry:2.6.2 localhost:5000/registry:2.6.0
|
||||
docker tag registry:2.6.2 localhost:5000/registry:2
|
||||
|
||||
docker push localhost:5000/registry
|
||||
25
examples/ui-as-standalone/registry-config/credentials.yml
Normal file
25
examples/ui-as-standalone/registry-config/credentials.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: 0.1
|
||||
log:
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
delete:
|
||||
enabled: true
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
http:
|
||||
addr: :5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
Access-Control-Allow-Origin: ['http://localhost']
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
|
||||
Access-Control-Allow-Headers: ['Authorization']
|
||||
Access-Control-Max-Age: [1728000]
|
||||
Access-Control-Allow-Credentials: [true]
|
||||
Access-Control-Expose-Headers: ['Docker-Content-Digest']
|
||||
auth:
|
||||
htpasswd:
|
||||
realm: basic-realm
|
||||
path: /etc/docker/registry/htpasswd
|
||||
1
examples/ui-as-standalone/registry-config/htpasswd
Normal file
1
examples/ui-as-standalone/registry-config/htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
registry:$2y$11$1bmuJLK8HrQl5ACS/WeqRuJLUArUZfUcP2R23asmozEpfN76.pCHy
|
||||
21
examples/ui-as-standalone/registry-config/simple.yml
Normal file
21
examples/ui-as-standalone/registry-config/simple.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
version: 0.1
|
||||
log:
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
delete:
|
||||
enabled: true
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
http:
|
||||
addr: :5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
Access-Control-Allow-Origin: ['http://localhost']
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
|
||||
Access-Control-Allow-Headers: ['Authorization']
|
||||
Access-Control-Max-Age: [1728000]
|
||||
Access-Control-Allow-Credentials: [true]
|
||||
Access-Control-Expose-Headers: ['Docker-Content-Digest']
|
||||
19
examples/ui-as-standalone/simple.yml
Normal file
19
examples/ui-as-standalone/simple.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
ports:
|
||||
- 5000:5000
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
- ./registry-config/simple.yml:/etc/docker/registry/config.yml
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
- URL=http://localhost:5000
|
||||
depends_on:
|
||||
- registry
|
||||
143
gulpfile.js
143
gulpfile.js
@@ -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-2019',
|
||||
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-2019',
|
||||
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-2019',
|
||||
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
43
nginx/default.conf
Normal 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;
|
||||
#}
|
||||
}
|
||||
34
package.json
34
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docker-registry-ui",
|
||||
"version": "0.2.2",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "./node_modules/gulp/bin/gulp.js build"
|
||||
},
|
||||
@@ -9,26 +9,26 @@
|
||||
"url": "https://github.com/Joxit/docker-registry-ui.git"
|
||||
},
|
||||
"author": "Jones Magloire (Joxit)",
|
||||
"license": "AGPLv3",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "A web UI for private docker registry",
|
||||
"dependencies": {
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"del": "^0.1.3",
|
||||
"dialog-polyfill": "^0.4",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 36 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
37
src/images/docker-logo.svg
Normal file
37
src/images/docker-logo.svg
Normal 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 |
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
Copyright (C) 2016 Jones Magloire @Joxit
|
||||
Copyright (C) 2016-2019 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,29 @@
|
||||
</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 & 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/catalog-element.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 -->
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jones Magloire @Joxit
|
||||
* Copyright (C) 2016-2019 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 + '`';
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jones Magloire @Joxit
|
||||
* Copyright (C) 2016-2019 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('*');
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jones Magloire @Joxit
|
||||
* Copyright (C) 2016-2019 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('*');
|
||||
});
|
||||
|
||||
423
src/style.css
423
src/style.css
@@ -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,409 @@ 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:hover {
|
||||
background-color: #eee;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.list > span,
|
||||
.list > li {
|
||||
box-sizing: border-box;
|
||||
line-height: 1;
|
||||
height: 48px;
|
||||
padding: 0 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.list > span i.material-icons,
|
||||
.list > li i.material-icons {
|
||||
margin-right: 32px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
font-size: 24px;
|
||||
box-sizing: border-box;
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.list > span .right i.material-icons.animated {
|
||||
transition: all 350ms cubic-bezier(.4,0,.2,1);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.list > span .right {
|
||||
position: absolute;
|
||||
align-self: end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.list > span i.material-icons.animated.expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.list > span .item-count {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.list > span,
|
||||
.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.list {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.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;
|
||||
z-index: 75;
|
||||
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;
|
||||
}
|
||||
|
||||
catalog material-card,
|
||||
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;
|
||||
}
|
||||
|
||||
catalog-element material-card {
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
catalog-element catalog-element material-card {
|
||||
transition: all 350ms cubic-bezier(.4,0,.2,1);
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
catalog-element catalog-element.showing material-card,
|
||||
catalog-element catalog-element.hide material-card {
|
||||
margin-top: -50px;
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -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>
|
||||
266
src/tags/app.tag
266
src/tags/app.tag
@@ -1,43 +1,241 @@
|
||||
<!--
|
||||
Copyright (C) 2016 Jones Magloire @Joxit
|
||||
Copyright (C) 2016-2019 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 & 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>
|
||||
59
src/tags/catalog-element.tag
Normal file
59
src/tags/catalog-element.tag
Normal file
@@ -0,0 +1,59 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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/>.
|
||||
-->
|
||||
<catalog-element>
|
||||
<!-- Begin of tag -->
|
||||
<material-card class="list highlight" item="{item}" expanded="{expanded}">
|
||||
<material-waves onmousedown="{launch}" center="true" color="#ddd" />
|
||||
<span>
|
||||
<i class="material-icons">send</i>
|
||||
{ typeof opts.item === "string" ? opts.item : opts.item.repo }
|
||||
<div hide="{typeof opts.item === "string"}" class="item-count right">
|
||||
{ opts.item.images && opts.item.images.length } images
|
||||
<i class="material-icons animated {expanded: opts.expanded}">expand_more</i>
|
||||
</div>
|
||||
</span>
|
||||
</material-card>
|
||||
<catalog-element hide="{typeof opts.item === "string"}" class="animated {hide: !expanded, expanding: expanding}" each="{item in item.images}" />
|
||||
<script>
|
||||
this.on('mount', function() {
|
||||
const self = this;
|
||||
const card = this.tags['material-card'];
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
// Launch waves
|
||||
card.launch = function(e) {
|
||||
card.tags['material-waves'].trigger('launch',e);
|
||||
}
|
||||
if (this.item.images && this.item.images.length === 1) {
|
||||
this.item = this.item.images[0];
|
||||
}
|
||||
card.root.onclick = function(e) {
|
||||
if (!self.item.repo) {
|
||||
registryUI.taglist.go(self.item);
|
||||
} else {
|
||||
self.expanded = !self.expanded;
|
||||
self.update({expanded: self.expanded, expanding: true});
|
||||
setTimeout(function() {
|
||||
self.update({expanded: self.expanded, expanding: false});
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!-- End of tag -->
|
||||
</catalog-element>
|
||||
@@ -1,90 +1,74 @@
|
||||
<!--
|
||||
Copyright (C) 2016 Jones Magloire @Joxit
|
||||
Copyright (C) 2016-2019 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.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>
|
||||
|
||||
</material-card>
|
||||
<catalog-element each="{ item in registryUI.catalog.repositories }" />
|
||||
<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();
|
||||
registryUI.catalog.length = registryUI.catalog.repositories.length; registryUI.catalog.repositories = registryUI.catalog.repositories.reduce(function(acc, e) {
|
||||
const slash = e.indexOf('/');
|
||||
if (slash > 0) {
|
||||
const repoName = e.substring(0, slash) + '/';
|
||||
if (acc.length == 0 || acc[acc.length - 1].repo != repoName) {
|
||||
acc.push({repo: repoName, images: []});
|
||||
}
|
||||
acc[acc.length - 1].images.push(e);
|
||||
return acc;
|
||||
}
|
||||
acc.push(e);
|
||||
return acc;
|
||||
}, []);
|
||||
} 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>
|
||||
|
||||
@@ -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>
|
||||
34
src/tags/copy-to-clipboard.tag
Normal file
34
src/tags/copy-to-clipboard.tag
Normal file
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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
59
src/tags/dialogs/add.tag
Normal file
@@ -0,0 +1,59 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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>
|
||||
61
src/tags/dialogs/change.tag
Normal file
61
src/tags/dialogs/change.tag
Normal file
@@ -0,0 +1,61 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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
45
src/tags/dialogs/menu.tag
Normal file
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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>
|
||||
61
src/tags/dialogs/remove.tag
Normal file
61
src/tags/dialogs/remove.tag
Normal file
@@ -0,0 +1,61 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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
43
src/tags/image-date.tag
Normal file
@@ -0,0 +1,43 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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
27
src/tags/image-size.tag
Normal file
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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
27
src/tags/image-tag.tag
Normal file
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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>
|
||||
@@ -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>
|
||||
@@ -1,63 +1,65 @@
|
||||
<!--
|
||||
Copyright (C) 2016 Jones Magloire @Joxit
|
||||
Copyright (C) 2016-2019 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>
|
||||
|
||||
@@ -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>
|
||||
31
src/tags/tag-history-button.tag
Normal file
31
src/tags/tag-history-button.tag
Normal file
@@ -0,0 +1,31 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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>
|
||||
64
src/tags/tag-history-element.tag
Normal file
64
src/tags/tag-history-element.tag
Normal file
@@ -0,0 +1,64 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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>
|
||||
138
src/tags/tag-history.tag
Normal file
138
src/tags/tag-history.tag
Normal file
@@ -0,0 +1,138 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 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) {
|
||||
window.scrollTo(0, 0);
|
||||
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>
|
||||
@@ -1,122 +1,121 @@
|
||||
<!--
|
||||
Copyright (C) 2016 Jones Magloire @Joxit
|
||||
Copyright (C) 2016-2019 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
38
static.dockerfile
Normal 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
|
||||
Reference in New Issue
Block a user