mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2026-02-17 21:19:51 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
d34d793b73 | ||
|
|
36cbda1263 | ||
|
|
2d9b3886de | ||
|
|
be553b0fb6 | ||
|
|
deb401a418 | ||
|
|
7adfd108da | ||
|
|
ded1a0be17 |
4
.dockerignore
Normal file
4
.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!dist
|
||||
!bin
|
||||
!nginx
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
.project
|
||||
node_modules
|
||||
package-lock.json
|
||||
registry-data
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2016 Jones Magloire @Joxit
|
||||
# Copyright (C) 2016-2018 Jones Magloire @Joxit
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
FROM nginx:alpine
|
||||
|
||||
MAINTAINER Jones MAGLOIRE @Joxit
|
||||
LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
|
||||
@@ -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
|
||||
73
README.md
73
README.md
@@ -5,9 +5,11 @@
|
||||
This project aims to provide a user interface for your private docker registry v2.
|
||||
There is no default registry on this UI, you should add your own with the UI.
|
||||
You can manage more than one registry server.
|
||||
All registry will be stored in the [local storage](https://en.wikipedia.org/wiki/Web_storage#Local_and_session_storage) of your browser.
|
||||
All registries will be stored in the [local storage](https://en.wikipedia.org/wiki/Web_storage#Local_and_session_storage) of your browser.
|
||||
|
||||
This web user interface use [Riot](https://github.com/Riot/riot) the react-like user interface micro-library and [Material Design Lite](https://github.com/google/material-design-lite) components.
|
||||
This web user interface uses [Riot](https://github.com/Riot/riot) the react-like user interface micro-library and [riot-mui](https://github.com/kysonic/riot-mui) components.
|
||||
|
||||
## [GitHub Page](https://joxit.github.io/docker-registry-ui) and [Live Demo](https://joxit.github.io/docker-registry-ui/demo/)
|
||||
|
||||

|
||||
|
||||
@@ -16,8 +18,15 @@ This web user interface use [Riot](https://github.com/Riot/riot) the react-like
|
||||
- List all your repositories/images.
|
||||
- List all tags for a repository/image
|
||||
- Sort the tag list
|
||||
- One interface for many registry
|
||||
- One interface for many registries
|
||||
- Use a secured docker registry
|
||||
- Share your docker registry with query parameter `url` (e.g. `https://joxit.github.io/docker-registry-ui/demo?url=https://registry.example.com`)
|
||||
- Use `joxit/docker-registry-ui:static` as reverse proxy to your docker registry (This will avoid CORS).
|
||||
- Display image size (see #30)
|
||||
- Add Title when using REGISTRY_URL (see #28)
|
||||
- Alpine and Debian based images with supports for arm32v7 and arm64v8
|
||||
- Copy `docker pull` command to clipbloard
|
||||
- Show sha256 for specific tag (hover image tag)
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -46,31 +55,43 @@ The docker contains the source code and a node webserver in order to serve the d
|
||||
|
||||
You can get the image in three ways
|
||||
|
||||
From sources with this command :
|
||||
From sources with this command:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/Joxit/docker-registry-ui.git
|
||||
docker build -t joxit/docker-registry-ui docker-registry-ui
|
||||
docker build -t joxit/docker-registry-ui -f docker-registry-ui/Dockerfile.static docker-registry-ui
|
||||
# Alpine
|
||||
docker build -t joxit/docker-registry-ui:latest docker-registry-ui
|
||||
docker build -t joxit/docker-registry-ui:static -f docker-registry-ui/static.dockerfile docker-registry-ui
|
||||
# Debian
|
||||
docker build -t joxit/docker-registry-ui:debian -f docker-registry-ui/debian.dockerfile docker-registry-ui
|
||||
docker build -t joxit/docker-registry-ui:static -f docker-registry-ui/debian-static.dockerfile docker-registry-ui
|
||||
```
|
||||
|
||||
Or build with the url :
|
||||
Or build with the url:
|
||||
|
||||
```sh
|
||||
docker build -t joxit/docker-registry-ui github.com/Joxit/docker-registry-ui
|
||||
docker build -t joxit/docker-registry-ui -f Dockerfile.static github.com/Joxit/docker-registry-ui
|
||||
# Alpine
|
||||
docker build -t joxit/docker-registry-ui:latest github.com/Joxit/docker-registry-ui
|
||||
docker build -t joxit/docker-registry-ui:static -f static.dockerfile github.com/Joxit/docker-registry-ui
|
||||
# Debian
|
||||
docker build -t joxit/docker-registry-ui:debian -f debian.dockerfile github.com/Joxit/docker-registry-ui
|
||||
docker build -t joxit/docker-registry-ui:debian-static -f debian-static.dockerfile github.com/Joxit/docker-registry-ui
|
||||
```
|
||||
|
||||
Or pull the image from [docker hub](https://hub.docker.com/r/joxit/docker-registry-ui/) :
|
||||
Or pull the image from [docker hub](https://hub.docker.com/r/joxit/docker-registry-ui/):
|
||||
|
||||
```sh
|
||||
docker pull joxit/docker-registry-ui
|
||||
# Alpine
|
||||
docker pull joxit/docker-registry-ui:latest
|
||||
docker pull joxit/docker-registry-ui:static
|
||||
# Debian
|
||||
docker pull joxit/docker-registry-ui:debian
|
||||
docker pull joxit/docker-registry-ui:debian-static
|
||||
```
|
||||
|
||||
#### Run the docker
|
||||
|
||||
To run the docker and see the website on your 80 port, try this :
|
||||
To run the docker and see the website on your 80 port, try this:
|
||||
|
||||
```sh
|
||||
docker run -d -p 80:80 joxit/docker-registry-ui
|
||||
@@ -80,22 +101,38 @@ docker run -d -p 80:80 joxit/docker-registry-ui
|
||||
|
||||
Some env options are available for use this interface for only one server.
|
||||
|
||||
- `URL`: set the static URL to use. (`Required`)
|
||||
- `DELETE_IMAGES`: if this variable is empty or `false`, delete feature is desactivated. It is activated otherwise.
|
||||
- `URL`: set the static URL to use (You will need CORS configuration). Example: `http://127.0.0.1:5000`. (`Required`)
|
||||
- `REGISTRY_URL`: your docker registry URL to contact (CORS configuration is not needed). Example: `http://my-docker-container:5000`. (Can't be used with `URL`, since 0.3.2).
|
||||
- `DELETE_IMAGES`: if this variable is empty or `false`, delete feature is deactivated. It is activated otherwise.
|
||||
- `REGISTRY_TITLE`: Set a custom title for your user interface when using `REGISTRY_URL` (since 0.3.4)
|
||||
|
||||
Example with `URL` option.
|
||||
|
||||
```sh
|
||||
docker run -d -p 80:80 -e URL=http://127.0.0.1:5000 -e DELETE_IMAGES=true joxit/docker-registry-ui:static
|
||||
```
|
||||
|
||||
Example with `REGISTRY_URL`, this will add a proxy to your registry.
|
||||
Your registry will be accessible here : `http://127.0.0.1/v2`, this will avoid CORS errors (see #25).
|
||||
Be careful, `joxit/docker-registry-ui` and `registry:2` will communicate, both containers should be in the same network or use your private IP.
|
||||
|
||||
```sh
|
||||
docker network create registry-ui-net
|
||||
docker run -d --net registry-ui-net --name registry-srv registry:2
|
||||
docker run -d --net registry-ui-net -p 80:80 -e REGISTRY_URL=http://registry-srv:5000 -e DELETE_IMAGES=true -e REGISTRY_TITLE="My registry" joxit/docker-registry-ui:static
|
||||
```
|
||||
|
||||
There are some examples with [docker-compose](https://docs.docker.com/compose/) and docker-registry-ui as proxy [here](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-proxy/).
|
||||
|
||||
## Using CORS
|
||||
|
||||
Your server should be configured to accept CORS.
|
||||
|
||||
If your docker registry does not need credentials, you will need to send this HEADER :
|
||||
If your docker registry does not need credentials, you will need to send this HEADER:
|
||||
|
||||
Access-Control-Allow-Origin: '*'
|
||||
|
||||
If your docker registry need credentials, you will need to send these HEADERS :
|
||||
If your docker registry need credentials, you will need to send these HEADERS:
|
||||
|
||||
```yml
|
||||
http:
|
||||
@@ -107,7 +144,7 @@ http:
|
||||
|
||||
## Using delete
|
||||
|
||||
For deleting images, you need to activate the delete feature in your registry :
|
||||
For deleting images, you need to activate the delete feature in your registry:
|
||||
|
||||
```yml
|
||||
storage:
|
||||
@@ -115,7 +152,7 @@ storage:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
And you need to add these HEADERS :
|
||||
And you need to add these HEADERS:
|
||||
|
||||
```yml
|
||||
http:
|
||||
|
||||
3
_config.yml
Normal file
3
_config.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
title: Docker Registry v2 User Interface
|
||||
google_analytics: UA-99119327-1
|
||||
theme: jekyll-theme-cayman
|
||||
26
arm32v7-static.dockerfile
Normal file
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/
|
||||
51
demo/index.html
Normal file
51
demo/index.html
Normal file
@@ -0,0 +1,51 @@
|
||||
<!--
|
||||
Copyright (C) 2016 Jones Magloire @Joxit
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="../dist/vendor.css">
|
||||
<link rel="stylesheet" href="../dist/style.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&lang=en" rel="stylesheet" type="text/css">
|
||||
<title>Docker Registry UI</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<app></app>
|
||||
<script src="../dist/scripts/vendor.js"></script>
|
||||
<script src="../dist/scripts/tags.js"></script>
|
||||
<script src="../dist/scripts/script.js"></script>
|
||||
<script>
|
||||
(function(i, s, o, g, r, a, m) {
|
||||
i['GoogleAnalyticsObject'] = r;
|
||||
i[r] = i[r] || function() {
|
||||
(i[r].q = i[r].q || []).push(arguments)
|
||||
}, i[r].l = 1 * new Date();
|
||||
a = s.createElement(o),
|
||||
m = s.getElementsByTagName(o)[0];
|
||||
a.async = 1;
|
||||
a.src = g;
|
||||
m.parentNode.insertBefore(a, m)
|
||||
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
|
||||
|
||||
ga('create', 'UA-99119327-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
2
dist/index.html
vendored
2
dist/index.html
vendored
@@ -13,4 +13,4 @@
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
--><!DOCTYPE html><html><head><meta charset="UTF-8"><link rel="stylesheet" href="vendor.css"><link rel="stylesheet" href="style.css"><link href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&lang=en" rel="stylesheet" type="text/css"><title>Docker Registry UI</title></head><body><!-- 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._events={},this._headers={}}Http.prototype.addEventListener=function(t,e){this._events[t]=e;var s=this;switch(t){case"loadend":s.oReq.addEventListener("loadend",function(){if(401==this.status){var t=new XMLHttpRequest;for(key in this.http._events)t.addEventListener(key,this.http._events[key]);for(key in this.http._headers)t.setRequestHeader(key,this.http._headers[key]);t.withCredentials=!0,t.open(this.http._method,this.http._url),t.send()}else e.bind(this)()});break;default:s.oReq.addEventListener(t,function(){e.bind(this)()})}},Http.prototype.setRequestHeader=function(t,e){this.oReq.setRequestHeader(t,e),this._headers[t]=e},Http.prototype.open=function(t,e){this._method=t,this._url=e,this.oReq.open(t,e)},Http.prototype.send=function(){this.oReq.http=this,this.oReq.send()};var registryUI={};registryUI.url=function(){return"${URL}"},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("catalog"),riot.mount("taglist"),riot.mount("app");
|
||||
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._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;var r=this;switch(e){case"loadend":r.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;for(key in this.http._events)e.addEventListener(key,this.http._events[key]);for(key in this.http._headers)e.setRequestHeader(key,this.http._headers[key]);e.withCredentials=!0,e.open(this.http._method,this.http._url),e.send()}else t.bind(this)()});break;default:r.oReq.addEventListener(e,function(){t.bind(this)()})}},Http.prototype.setRequestHeader=function(e,t){this.oReq.setRequestHeader(e,t),this._headers[e]=t},Http.prototype.open=function(e,t){this._method=e,this._url=t,this.oReq.open(e,t)},Http.prototype.send=function(){this.oReq.http=this,this.oReq.send()};var registryUI={};registryUI.url=function(){return registryUI.getRegistryServer(0)},registryUI.getRegistryServer=function(e){try{var t=JSON.parse(localStorage.getItem("registryServer"));if(t instanceof Array)return isNaN(e)?t.map(function(e){return e.trim().replace(/\/*$/,"")}):t[e]}catch(r){}return isNaN(e)?[]:""},registryUI.addServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);-1==r&&(t.push(e),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.changeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);-1!=r&&(t.splice(r,1),t=[e].concat(t),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.removeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);-1!=r&&(t.splice(r,1),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("add"),riot.mount("change"),riot.mount("remove"),riot.mount("menu"),riot.mount("app");
|
||||
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
551
dist/scripts/vendor.js
vendored
551
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
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* docker-registry-ui
|
||||
* Copyright (C) 2016 Jones Magloire @Joxit
|
||||
* Copyright (C) 2016-2018 Jones Magloire @Joxit
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -15,4 +15,4 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(MaterialIcons-Regular.eot);src:local('fonts/Material Icons'),local('fonts/MaterialIcons-Regular'),url(fonts/MaterialIcons-Regular.woff2) format('woff2'),url(fonts/MaterialIcons-Regular.woff) format('woff'),url(fonts/MaterialIcons-Regular.ttf) format('truetype')}.material-icons{font-family:'Material Icons';font-weight:400;font-style:normal;font-size:24px;display:inline-block;width:1em;height:1em;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'}html>body{font-family:Roboto,Helvetica,Arial,sans-serif!important}.mdl-mini-footer{padding-top:8px;padding-bottom:8px}.catalog,.taglist{padding:16px}.section-centerd{margin:auto}.mdl-data-table th{font-size:18px}.mdl-data-table td{font-size:16px}.full-table{width:100%;border:none}.url{font-size:14px;word-break:break-all}
|
||||
@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(MaterialIcons-Regular.eot);src:local('fonts/Material Icons'),local('fonts/MaterialIcons-Regular'),url(fonts/MaterialIcons-Regular.woff2) format('woff2'),url(fonts/MaterialIcons-Regular.woff) format('woff'),url(fonts/MaterialIcons-Regular.ttf) format('truetype')}.material-icons{font-family:'Material Icons';font-weight:400;font-style:normal;font-size:24px;display:inline-block;width:1em;height:1em;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'}html>body{font-family:Roboto,Helvetica,Arial,sans-serif!important}body,html{margin:0;height:100%}main{margin-bottom:100px}.section-centerd{margin:auto}.full-table{width:100%;border:none}.url{font-size:14px;word-break:break-all}.material-card-title-action a{color:inherit;text-decoration:none;font-weight:inherit}material-card{min-height:200px;max-width:75%;margin:auto;margin-top:20px;margin-bottom:20px}material-spinner{align-self:center}.spinner-wrapper{margin-top:50px;display:flex;flex-direction:column}material-navbar{height:64px}.logo{padding:0 16px 0 72px;text-decoration:none;font-size:20px;line-height:1;letter-spacing:.02em;font-weight:400}h2{padding:16px;margin:auto;font-size:24px;font-weight:300;line-height:normal;overflow:hidden}.list{display:block;padding:8px 0;list-style:none}.list.highlight>li:hover{background-color:#eee;cursor:pointer}.list>li{box-sizing:border-box;line-height:1;height:48px;padding:0 16px;overflow:hidden}.list>li i.material-icons{margin-right:32px;height:24px;width:24px;font-size:24px;box-sizing:border-box;color:#757575}.list>li>span{height:100%;text-decoration:none;box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.material-card-title-action{-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:block;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:16px}.material-card-title-action h2{margin:0}material-card table{width:100%;border:none;position:relative;border:1px solid rgba(0,0,0,.12);border-collapse:collapse;white-space:nowrap;font-size:13px;background-color:#fff;border:none}material-card table th{font-size:18px;vertical-align:bottom;line-height:24px;height:48px;color:rgba(0,0,0,.54);box-sizing:border-box;padding:0 18px 12px 18px;text-align:right}.material-card-th-left{text-align:left}material-card table tbody tr:hover{background-color:#eee}material-card table tbody tr{position:relative;height:48px;transition-duration:.28s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-property:background-color}material-card table td{font-size:16px;position:relative;height:48px;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);padding:12px 18px;box-sizing:border-box;vertical-align:middle;text-align:right}material-card table th.material-card-th-sorted-ascending:hover,material-card table th.material-card-th-sorted-descending:hover{cursor:pointer}material-card table th.material-card-th-sorted-ascending:hover:before,material-card table th.material-card-th-sorted-descending:hover:before{color:rgba(0,0,0,.26)}material-card table th.material-card-th-sorted-ascending:before,material-card table th.material-card-th-sorted-descending:before{font-family:'Material Icons';font-weight:400;font-style:normal;line-height:1;font-size:16px;content:"\e5d8";margin-right:5px;vertical-align:sub}material-card table th.material-card-th-sorted-descending:before{content:"\e5db"}.material-icons{color:#777}material-snackbar .toast{height:auto}menu{position:absolute;top:0;right:16px;color:#000}menu .overlay{position:fixed;height:100%;width:100%;top:0;right:0;z-index:1}#menu-control-button{background:rgba(255,255,255,0);float:right}#menu-control-button i{color:#fff;font-size:24px}#menu-control-dropdown{display:inline-block;position:relative}.dropdown{min-width:124px;padding:8px 0;margin:0}#menu-control-dropdown p,dropdown-item{padding:0 16px;margin:auto;line-height:48px;height:48px;cursor:pointer}#menu-control-dropdown p:hover{background-color:#eee}#menu-control-dropdown p:active,.material-button-active:active{background-color:#e0e0e0}material-popup material-button{background-color:#fff;color:#000}material-popup material-button:hover material-waves{background-color:hsla(0,0%,75%,.2)}material-popup .popup{max-width:450px}footer{width:100%;position:fixed;bottom:0}.select-padding{padding:20px 0}select{position:relative;outline:0;box-shadow:none;padding:0;width:100%;background:0 0;border:none;font-weight:400;line-height:24px;height:24px;border-bottom:1px solid #2f6975;appearance:none;-moz-appearance:none;-webkit-appearance:none}.copy-to-clipboard{padding:12px 5px}.copy-to-clipboard a:hover{cursor:pointer}
|
||||
56
dist/vendor.css
vendored
56
dist/vendor.css
vendored
File diff suppressed because one or more lines are too long
21
examples/ui-as-proxy/README.md
Normal file
21
examples/ui-as-proxy/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Docker Registry Static as proxy example
|
||||
|
||||
You can set up the static user interface as proxy in several ways.
|
||||
|
||||
If you want to populate your registry, use `populate.sh` script.
|
||||
The interface 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:
|
||||
112
gulpfile.js
112
gulpfile.js
@@ -1,23 +1,47 @@
|
||||
'use strict';
|
||||
var cleanCSS = require('gulp-clean-css');
|
||||
var concat = require('gulp-concat');
|
||||
var del = require('del');
|
||||
var filter = require('gulp-filter');
|
||||
var fs = require('fs');
|
||||
var gIf = require('gulp-if');
|
||||
var gulp = require('gulp');
|
||||
var htmlmin = require('gulp-htmlmin');
|
||||
var license = require('gulp-license');
|
||||
var riot = require('gulp-riot');
|
||||
var minifier = require('gulp-uglify/minifier');
|
||||
var uglify = require('uglify-js-harmony');
|
||||
var useref = require('gulp-useref');
|
||||
const cleanCSS = require('gulp-clean-css');
|
||||
const concat = require('gulp-concat');
|
||||
const del = require('del');
|
||||
const filter = require('gulp-filter');
|
||||
const fs = require('fs');
|
||||
const gIf = require('gulp-if');
|
||||
const gulp = require('gulp');
|
||||
const htmlmin = require('gulp-htmlmin');
|
||||
const license = require('gulp-license');
|
||||
const riot = require('gulp-riot');
|
||||
const uglify = require('uglify-es');
|
||||
const minifier = require('gulp-uglify/composer')(uglify);
|
||||
const useref = require('gulp-useref');
|
||||
const injectVersion = require('gulp-inject-version');
|
||||
const merge = require('stream-series');
|
||||
|
||||
const allTags = 'src/tags/*.tag';
|
||||
|
||||
const allScripts = [
|
||||
'src/scripts/http.js',
|
||||
'src/scripts/script.js'
|
||||
];
|
||||
|
||||
const staticTags = [
|
||||
'src/tags/catalog.tag',
|
||||
'src/tags/app.tag',
|
||||
'src/tags/taglist.tag',
|
||||
'src/tags/copy-to-clipboard.tag',
|
||||
'src/tags/remove-image.tag',
|
||||
'src/tags/image-size.tag',
|
||||
'src/tags/image-tag.tag'
|
||||
];
|
||||
|
||||
const staticScripts = [
|
||||
'src/scripts/http.js',
|
||||
'src/scripts/static.js'
|
||||
];
|
||||
|
||||
gulp.task('html', function() {
|
||||
var htmlFilter = filter('**/*.html');
|
||||
var htmlFilter = filter('**/*.html', {restore: true});
|
||||
return gulp.src(['src/index.html'])
|
||||
.pipe(useref())
|
||||
.pipe(gIf(['*.js', '!*.min.js'], minifier({}, uglify))) // FIXME
|
||||
.pipe(gIf(['*.js', '!*.min.js'], minifier())) // FIXME
|
||||
.pipe(htmlFilter)
|
||||
.pipe(htmlmin({
|
||||
removeComments: false,
|
||||
@@ -26,65 +50,45 @@ gulp.task('html', function() {
|
||||
removeEmptyAttributes: true,
|
||||
minifyJS: uglify
|
||||
}))
|
||||
.pipe(htmlFilter.restore())
|
||||
.pipe(htmlFilter.restore)
|
||||
.pipe(gulp.dest('dist'));
|
||||
});
|
||||
|
||||
gulp.task('clean', function(done) {
|
||||
del(['dist'], done);
|
||||
return del(['dist']);
|
||||
});
|
||||
|
||||
gulp.task('riot-tag', ['html'], function() {
|
||||
return gulp.src('src/tags/*.tag')
|
||||
.pipe(concat('tags.js'))
|
||||
.pipe(riot())
|
||||
.pipe(minifier({}, uglify))
|
||||
gulp.task('docker-registry-ui-static', ['html'], function() {
|
||||
return merge(gulp.src(staticScripts), gulp.src(staticTags).pipe(riot()))
|
||||
.pipe(concat('docker-registry-ui-static.js'))
|
||||
.pipe(minifier())
|
||||
.pipe(license('agpl3', {
|
||||
tiny: false,
|
||||
project: 'docker-registry-ui',
|
||||
year: '2016',
|
||||
year: '2016-2018',
|
||||
organization: 'Jones Magloire @Joxit'
|
||||
}))
|
||||
.pipe(injectVersion())
|
||||
.pipe(gulp.dest('dist/scripts'));
|
||||
});
|
||||
|
||||
gulp.task('riot-static-tag', ['html'], function() {
|
||||
return gulp.src(['src/tags/catalog.tag', 'src/tags/app.tag', 'src/tags/taglist.tag', 'src/tags/remove-image.tag'])
|
||||
.pipe(concat('tags-static.js'))
|
||||
.pipe(riot())
|
||||
.pipe(minifier({}, uglify))
|
||||
gulp.task('docker-registry-ui', ['html'], function() {
|
||||
return merge(gulp.src(allScripts), gulp.src(allTags).pipe(riot()))
|
||||
.pipe(concat('docker-registry-ui.js'))
|
||||
.pipe(minifier())
|
||||
.pipe(license('agpl3', {
|
||||
tiny: false,
|
||||
project: 'docker-registry-ui',
|
||||
year: '2016',
|
||||
year: '2016-2018',
|
||||
organization: 'Jones Magloire @Joxit'
|
||||
}))
|
||||
.pipe(injectVersion())
|
||||
.pipe(gulp.dest('dist/scripts'));
|
||||
});
|
||||
|
||||
gulp.task('scripts-static', ['html'], function() {
|
||||
return gulp.src(['src/scripts/http.js', 'src/scripts/static.js'])
|
||||
.pipe(concat('script-static.js'))
|
||||
.pipe(minifier({}, uglify))
|
||||
.pipe(license('agpl3', {
|
||||
tiny: false,
|
||||
project: 'docker-registry-ui',
|
||||
year: '2016',
|
||||
organization: 'Jones Magloire @Joxit'
|
||||
}))
|
||||
.pipe(gulp.dest('dist/scripts'));
|
||||
});
|
||||
|
||||
gulp.task('scripts', ['html'], function() {
|
||||
return gulp.src(['src/scripts/http.js', 'src/scripts/script.js'])
|
||||
.pipe(concat('script.js'))
|
||||
.pipe(minifier({}, uglify))
|
||||
.pipe(license('agpl3', {
|
||||
tiny: false,
|
||||
project: 'docker-registry-ui',
|
||||
year: '2016',
|
||||
organization: 'Jones Magloire @Joxit'
|
||||
}))
|
||||
gulp.task('vendor', ['html'], function() {
|
||||
return gulp.src(['node_modules/riot/riot.min.js', 'node_modules/riot-route/dist/route.min.js', 'node_modules/riot-mui/build/js/riot-mui-min.js'])
|
||||
.pipe(concat('vendor.js'))
|
||||
.pipe(gulp.dest('dist/scripts'));
|
||||
});
|
||||
|
||||
@@ -97,7 +101,7 @@ gulp.task('styles', ['html'], function() {
|
||||
.pipe(license('agpl3', {
|
||||
tiny: false,
|
||||
project: 'docker-registry-ui',
|
||||
year: '2016',
|
||||
year: '2016-2018',
|
||||
organization: 'Jones Magloire @Joxit'
|
||||
}))
|
||||
.pipe(gulp.dest('dist/'));
|
||||
@@ -109,7 +113,7 @@ gulp.task('fonts', function() {
|
||||
.pipe(gulp.dest('dist/fonts'));
|
||||
});
|
||||
|
||||
gulp.task('sources', ['riot-tag', 'riot-static-tag', 'scripts', 'scripts-static', 'styles'], function() {
|
||||
gulp.task('sources', ['docker-registry-ui', 'vendor', 'docker-registry-ui-static', 'styles'], function() {
|
||||
gulp.start();
|
||||
});
|
||||
|
||||
|
||||
43
nginx/default.conf
Normal file
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;
|
||||
#}
|
||||
}
|
||||
32
package.json
32
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docker-registry-ui",
|
||||
"version": "0.2.0",
|
||||
"version": "0.4.0",
|
||||
"scripts": {
|
||||
"build": "./node_modules/gulp/bin/gulp.js build"
|
||||
},
|
||||
@@ -9,26 +9,26 @@
|
||||
"url": "https://github.com/Joxit/docker-registry-ui.git"
|
||||
},
|
||||
"author": "Jones Magloire (Joxit)",
|
||||
"license": "AGPLv3",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "A web UI for private docker registry",
|
||||
"dependencies": {
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"del": "^0.1.3",
|
||||
"dialog-polyfill": "^0.4",
|
||||
"del": "^3.0.0",
|
||||
"gulp": "^3.9",
|
||||
"gulp-clean-css": "^3.9.4",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-filter": "^1.0.0",
|
||||
"gulp-htmlmin": "^2.0.0",
|
||||
"gulp-filter": "^5.1.0",
|
||||
"gulp-htmlmin": "^3.0.0",
|
||||
"gulp-if": "^2.0.0",
|
||||
"gulp-inject-version": "^1.0.1",
|
||||
"gulp-license": "^1.1.0",
|
||||
"gulp-clean-css": "^2.0.11",
|
||||
"gulp-riot": "^0.5.2",
|
||||
"gulp-uglify": "^1.0.0",
|
||||
"gulp-useref": "^3.0.0",
|
||||
"material-design-lite": "^1.1",
|
||||
"riot": "^2.3",
|
||||
"riotgear-router": "^1.3.1",
|
||||
"uglify-js-harmony": "^2.6.2"
|
||||
"gulp-riot": "^1.1.4",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"gulp-useref": "^3.1.5",
|
||||
"riot": "^3.11.1",
|
||||
"riot-mui": "^0.1.1",
|
||||
"riot-route": "^3.1.3",
|
||||
"stream-series": "^0.1.1",
|
||||
"uglify-es": "^3.3.10"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 38 KiB |
@@ -20,8 +20,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- build:css vendor.css -->
|
||||
<LINK href="../node_modules/dialog-polyfill/dialog-polyfill.css" rel="stylesheet" type="text/css">
|
||||
<LINK href="../node_modules/material-design-lite/dist/material.min.css" rel="stylesheet" type="text/css">
|
||||
<LINK href="../node_modules/riot-mui/build/styles/riot-mui.min.css" rel="stylesheet" type="text/css">
|
||||
<!-- endbuild -->
|
||||
<!-- build:css style.css -->
|
||||
<LINK href="style.css" rel="stylesheet" type="text/css">
|
||||
@@ -32,50 +31,24 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Always shows a header, even in smaller screens. -->
|
||||
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
|
||||
<header class="mdl-layout__header">
|
||||
<div class="mdl-layout__header-row">
|
||||
<!-- Title -->
|
||||
<span class="mdl-layout-title">Docker Registry UI</span>
|
||||
<menu></menu>
|
||||
</div>
|
||||
</header>
|
||||
<main class="mdl-layout__content">
|
||||
<div class="page-content">
|
||||
<app></app>
|
||||
</div>
|
||||
</main>
|
||||
<change></change>
|
||||
<add></add>
|
||||
<remove></remove>
|
||||
<footer class="mdl-mini-footer">
|
||||
<div class="mdl-mini-footer__left-section">
|
||||
<div class="mdl-logo">Docker Registry UI</div>
|
||||
<ul class="mdl-mini-footer__link-list">
|
||||
<li><a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a></li>
|
||||
<li><a href="https://github.com/Joxit/docker-registry-ui/blob/master/LICENSE">Privacy & Terms</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<app></app>
|
||||
<!-- build:js scripts/vendor.js -->
|
||||
<script src="../node_modules/riot/riot+compiler.min.js"></script>
|
||||
<script src="../node_modules/riotgear-router/dist/rg-router.min.js"></script>
|
||||
<script src="../node_modules/dialog-polyfill/dialog-polyfill.js"></script>
|
||||
<script src="../node_modules/material-design-lite/dist/material.min.js"></script>
|
||||
<script src="../node_modules/riot-route/dist/route.js"></script>
|
||||
<script src="../node_modules/riot-mui/build/js/riot-mui.js"></script>
|
||||
<!-- endbuild -->
|
||||
<!-- build:js scripts/tags.js -->
|
||||
<!-- build:js scripts/docker-registry-ui.js -->
|
||||
<script src="tags/catalog.tag" type="riot/tag"></script>
|
||||
<script src="tags/taglist.tag" type="riot/tag"></script>
|
||||
<script src="tags/image-tag.tag" type="riot/tag"></script>
|
||||
<script src="tags/remove-image.tag" type="riot/tag"></script>
|
||||
<script src="tags/copy-to-clipboard.tag" type="riot/tag"></script>
|
||||
<script src="tags/add.tag" type="riot/tag"></script>
|
||||
<script src="tags/change.tag" type="riot/tag"></script>
|
||||
<script src="tags/remove.tag" type="riot/tag"></script>
|
||||
<script src="tags/menu.tag" type="riot/tag"></script>
|
||||
<script src="tags/image-size.tag" type="riot/tag"></script>
|
||||
<script src="tags/app.tag" type="riot/tag"></script>
|
||||
<!-- endbuild -->
|
||||
<!-- build:js scripts/script.js -->
|
||||
<script src="scripts/http.js"></script>
|
||||
<script src="scripts/script.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
*/
|
||||
function Http() {
|
||||
this.oReq = new XMLHttpRequest();
|
||||
this.oReq.hasHeader = Http.hasHeader;
|
||||
this.oReq.getErrorMessage = Http.getErrorMessage;
|
||||
this._events = {};
|
||||
this._headers = {};
|
||||
}
|
||||
@@ -29,14 +31,16 @@ Http.prototype.addEventListener = function(e, f) {
|
||||
self.oReq.addEventListener('loadend', function() {
|
||||
if (this.status == 401) {
|
||||
var req = new XMLHttpRequest();
|
||||
for (key in this.http._events) {
|
||||
req.addEventListener(key, this.http._events[key]);
|
||||
req.open(self._method, self._url);
|
||||
for (key in self._events) {
|
||||
req.addEventListener(key, self._events[key]);
|
||||
}
|
||||
for (key in this.http._headers) {
|
||||
req.setRequestHeader(key, this.http._headers[key]);
|
||||
for (key in self._headers) {
|
||||
req.setRequestHeader(key, self._headers[key]);
|
||||
}
|
||||
req.withCredentials = true;
|
||||
req.open(this.http._method, this.http._url);
|
||||
req.hasHeader = Http.hasHeader;
|
||||
req.getErrorMessage = Http.getErrorMessage;
|
||||
req.send();
|
||||
} else {
|
||||
f.bind(this)();
|
||||
@@ -44,6 +48,15 @@ Http.prototype.addEventListener = function(e, f) {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'load':
|
||||
{
|
||||
self.oReq.addEventListener('load', function() {
|
||||
if (this.status !== 401) {
|
||||
f.bind(this)();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
self.oReq.addEventListener(e, function() {
|
||||
@@ -66,6 +79,22 @@ Http.prototype.open = function(m, u) {
|
||||
};
|
||||
|
||||
Http.prototype.send = function() {
|
||||
this.oReq.http = this;
|
||||
this.oReq.send();
|
||||
};
|
||||
|
||||
Http.hasHeader = function(header) {
|
||||
return this.getAllResponseHeaders().split('\n').some(function(h) {
|
||||
return new RegExp('^' + header + ':', 'i').test(h);
|
||||
});
|
||||
};
|
||||
|
||||
Http.getErrorMessage = function() {
|
||||
if (registryUI.url() && registryUI.url().match('^http://') && window.location.protocol === 'https:') {
|
||||
return 'Mixed Content: The page at `' + window.location.origin + '` was loaded over HTTPS, but requested an insecure server endpoint `' + registryUI.url() + '`. This request has been blocked; the content must be served over HTTPS.';
|
||||
} else if (!registryUI.url()) {
|
||||
return 'Incorrect server endpoint.';
|
||||
} else if (this.withCredentials && !this.hasHeader('Access-Control-Allow-Credentials')) {
|
||||
return 'The `Access-Control-Allow-Credentials` header in the response is missing and must be set to `true` when the request\'s credentials mode is on. Origin `'+ registryUI.url() +'` is therefore not allowed access.';
|
||||
}
|
||||
return 'An error occured: Check your connection and your registry must have `Access-Control-Allow-Origin` header set to `' + window.location.origin + '`';
|
||||
};
|
||||
@@ -15,8 +15,23 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
var registryUI = {}
|
||||
registryUI.url = function() {
|
||||
return registryUI.getRegistryServer(0);
|
||||
registryUI.URL_QUERY_PARAM_REGEX = /[&?]url=/;
|
||||
registryUI.URL_PARAM_REGEX = /^url=/;
|
||||
|
||||
registryUI.name = registryUI.url = function(byPassQueryParam) {
|
||||
if (!registryUI._url) {
|
||||
var url = registryUI.getUrlQueryParam();
|
||||
if (url) {
|
||||
try {
|
||||
registryUI._url = registryUI.decodeURI(url);
|
||||
return registryUI._url;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
registryUI._url = registryUI.getRegistryServer(0);
|
||||
}
|
||||
return registryUI._url;
|
||||
}
|
||||
registryUI.getRegistryServer = function(i) {
|
||||
try {
|
||||
@@ -37,6 +52,9 @@ registryUI.addServer = function(url) {
|
||||
return;
|
||||
}
|
||||
registryServer.push(url);
|
||||
if (!registryUI._url) {
|
||||
registryUI.updateHistory(url);
|
||||
}
|
||||
localStorage.setItem('registryServer', JSON.stringify(registryServer));
|
||||
}
|
||||
registryUI.changeServer = function(url) {
|
||||
@@ -48,6 +66,7 @@ registryUI.changeServer = function(url) {
|
||||
}
|
||||
registryServer.splice(index, 1);
|
||||
registryServer = [url].concat(registryServer);
|
||||
registryUI.updateHistory(url);
|
||||
localStorage.setItem('registryServer', JSON.stringify(registryServer));
|
||||
}
|
||||
registryUI.removeServer = function(url) {
|
||||
@@ -59,13 +78,39 @@ registryUI.removeServer = function(url) {
|
||||
}
|
||||
registryServer.splice(index, 1);
|
||||
localStorage.setItem('registryServer', JSON.stringify(registryServer));
|
||||
if (url == registryUI.url()) {
|
||||
registryUI.updateHistory(registryUI.getRegistryServer(0));
|
||||
route('');
|
||||
}
|
||||
}
|
||||
|
||||
registryUI.updateHistory = function(url) {
|
||||
history.pushState(null, '', (url ? '?url=' + registryUI.encodeURI(url) : '?') + window.location.hash);
|
||||
registryUI._url = url;
|
||||
}
|
||||
|
||||
registryUI.getUrlQueryParam = function () {
|
||||
var search = window.location.search;
|
||||
if (registryUI.URL_QUERY_PARAM_REGEX.test(search)) {
|
||||
var param = search.split(/^\?|&/).find(function(param) {
|
||||
return param && registryUI.URL_PARAM_REGEX.test(param);
|
||||
});
|
||||
return param ? param.replace(registryUI.URL_PARAM_REGEX, '') : param;
|
||||
}
|
||||
};
|
||||
|
||||
registryUI.encodeURI = function(url) {
|
||||
return url.indexOf('&') < 0 ? window.encodeURIComponent(url) : btoa(url);
|
||||
};
|
||||
|
||||
registryUI.decodeURI = function(url) {
|
||||
return url.startsWith('http') ? window.decodeURIComponent(url) : atob(url);
|
||||
};
|
||||
|
||||
registryUI.isImageRemoveActivated = true;
|
||||
registryUI.catalog = {};
|
||||
registryUI.taglist = {};
|
||||
|
||||
riot.mount('add');
|
||||
riot.mount('change');
|
||||
riot.mount('remove');
|
||||
riot.mount('menu');
|
||||
riot.mount('app');
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
riot.mount('*');
|
||||
});
|
||||
|
||||
@@ -18,10 +18,13 @@ var registryUI = {}
|
||||
registryUI.url = function() {
|
||||
return '${URL}';
|
||||
};
|
||||
registryUI.name = function() {
|
||||
return '${REGISTRY_TITLE}' || registryUI.url();
|
||||
};
|
||||
registryUI.isImageRemoveActivated = true;
|
||||
registryUI.catalog = {};
|
||||
registryUI.taglist = {};
|
||||
|
||||
riot.mount('catalog');
|
||||
riot.mount('taglist');
|
||||
riot.mount('app');
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
riot.mount('*');
|
||||
});
|
||||
|
||||
304
src/style.css
304
src/style.css
@@ -15,31 +15,23 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
html > body {
|
||||
html>body {
|
||||
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif !important;
|
||||
}
|
||||
|
||||
.mdl-mini-footer {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.catalog, .taglist {
|
||||
padding: 16px;
|
||||
main {
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
|
||||
.section-centerd {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.mdl-data-table th {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.mdl-data-table td {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.full-table {
|
||||
width: 100%;
|
||||
border: none;
|
||||
@@ -48,4 +40,288 @@ html > body {
|
||||
.url {
|
||||
font-size: 14px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.material-card-title-action a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
material-card {
|
||||
min-height: 200px;
|
||||
max-width: 75%;
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
material-spinner {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.spinner-wrapper {
|
||||
margin-top: 50px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
material-navbar {
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
padding: 0 16px 0 72px;
|
||||
text-decoration: none;
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
letter-spacing: .02em;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding: 16px;
|
||||
margin: auto;
|
||||
font-size: 24px;
|
||||
font-weight: 300;
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: block;
|
||||
padding: 8px 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.list.highlight>li:hover {
|
||||
background-color: #eee;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.list>li {
|
||||
box-sizing: border-box;
|
||||
line-height: 1;
|
||||
height: 48px;
|
||||
padding: 0 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.list>li i.material-icons {
|
||||
margin-right: 32px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
font-size: 24px;
|
||||
box-sizing: border-box;
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.list>li>span {
|
||||
height: 100%;
|
||||
text-decoration: none;
|
||||
box-sizing: border-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.material-card-title-action {
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
display: block;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.material-card-title-action h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
material-card table {
|
||||
width: 100%;
|
||||
border: none;
|
||||
position: relative;
|
||||
border: 1px solid rgba(0, 0, 0, .12);
|
||||
border-collapse: collapse;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
material-card table th {
|
||||
font-size: 18px;
|
||||
vertical-align: bottom;
|
||||
line-height: 24px;
|
||||
height: 48px;
|
||||
color: rgba(0, 0, 0, .54);
|
||||
box-sizing: border-box;
|
||||
padding: 0 18px 12px 18px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.material-card-th-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
material-card table tbody tr:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
material-card table tbody tr {
|
||||
position: relative;
|
||||
height: 48px;
|
||||
transition-duration: .28s;
|
||||
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
|
||||
transition-property: background-color;
|
||||
}
|
||||
|
||||
material-card table td {
|
||||
font-size: 16px;
|
||||
position: relative;
|
||||
height: 48px;
|
||||
border-top: 1px solid rgba(0, 0, 0, .12);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .12);
|
||||
padding: 12px 18px;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
material-card table th.material-card-th-sorted-ascending:hover, material-card table th.material-card-th-sorted-descending:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
material-card table th.material-card-th-sorted-ascending:hover:before, material-card table th.material-card-th-sorted-descending:hover:before {
|
||||
color: rgba(0, 0, 0, .26);
|
||||
}
|
||||
|
||||
material-card table th.material-card-th-sorted-ascending:before, material-card table th.material-card-th-sorted-descending:before {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 1;
|
||||
font-size: 16px;
|
||||
content: "\e5d8";
|
||||
margin-right: 5px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
material-card table th.material-card-th-sorted-descending:before {
|
||||
content: "\e5db";
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
material-snackbar .toast {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
menu {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 16px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
menu .overlay {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#menu-control-button {
|
||||
background: rgba(255, 255, 255, 0);
|
||||
float: right;
|
||||
}
|
||||
|
||||
#menu-control-button i {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
#menu-control-dropdown {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
min-width: 124px;
|
||||
padding: 8px 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
dropdown-item, #menu-control-dropdown p {
|
||||
padding: 0 16px;
|
||||
margin: auto;
|
||||
line-height: 48px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#menu-control-dropdown p:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#menu-control-dropdown p:active, .material-button-active:active {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
material-popup material-button {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
material-popup material-button:hover material-waves {
|
||||
background-color: hsla(0, 0%, 75%, .2);
|
||||
}
|
||||
|
||||
material-popup .popup {
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.select-padding {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
select {
|
||||
position: relative;
|
||||
outline: 0;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
background: 0 0;
|
||||
border: none;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
height: 24px;
|
||||
border-bottom: 1px solid #2f6975;
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.copy-to-clipboard {
|
||||
padding: 12px 5px;
|
||||
}
|
||||
|
||||
.copy-to-clipboard a:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -15,51 +15,45 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<add>
|
||||
<dialog id="add-server-dialog" class="mdl-dialog">
|
||||
<h4 class="mdl-dialog__title">Add your Server ?</h4>
|
||||
<div class="mdl-dialog__content">
|
||||
<div class="mdl-textfield mdl-js-textfield">
|
||||
<input class="mdl-textfield__input" type="text" id="add-server-input">
|
||||
<label class="mdl-textfield__label" for="add-server-input">Server url</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdl-dialog__actions">
|
||||
<button type="button" class="mdl-button change" onClick="registryUI.addTag.add();">Add</button>
|
||||
<button type="button" class="mdl-button close" onClick="registryUI.addTag.close();">Cancel</button>
|
||||
</div>
|
||||
</dialog>
|
||||
<material-popup>
|
||||
<div class="material-popup-title">Add your Server ?</div>
|
||||
<div class="material-popup-content">
|
||||
<material-input onkeyup="{ registryUI.addTag.onkeyup }" placeholder="Server URL"></material-input>
|
||||
<span>Write your URL without /v2</span>
|
||||
</div>
|
||||
<div class="material-popup-action">
|
||||
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.addTag.add();">Add</material-button>
|
||||
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.addTag.close();">Cancel</material-button>
|
||||
</div>
|
||||
</material-popup>
|
||||
|
||||
<script type="text/javascript">
|
||||
registryUI.addTag = registryUI.addTag || {};
|
||||
registryUI.addTag.update = this.update;
|
||||
this.on('updated', function () {
|
||||
componentHandler.upgradeElements(this['add-server-dialog']);
|
||||
registryUI.addTag.dialog = registryUI.addTag.dialog || document.querySelector('#add-server-dialog');
|
||||
registryUI.addTag.addServer = registryUI.addTag.tileServerList || registryUI.addTag.dialog.querySelector('#add-server-input');
|
||||
if (!registryUI.addTag.dialog.showModal) {
|
||||
dialogPolyfill.registerDialog(registryUI.addTag.dialog);
|
||||
this.one('mount', function () {
|
||||
registryUI.addTag.dialog = this.tags['material-popup'];
|
||||
registryUI.addTag.dialog.getAddServer = function() {
|
||||
return this.tags['material-input'] ? this.tags['material-input'].value : '';
|
||||
}
|
||||
this['add-server-input'].onkeyup = function (e) {
|
||||
// if keyCode is Enter
|
||||
if (e.keyCode == 13) {
|
||||
registryUI.addTag.add();
|
||||
}
|
||||
};
|
||||
});
|
||||
registryUI.addTag.onkeyup = function (e) {
|
||||
// if keyCode is Enter
|
||||
if (e.keyCode == 13) {
|
||||
registryUI.addTag.add();
|
||||
}
|
||||
};
|
||||
registryUI.addTag.show = function () {
|
||||
registryUI.addTag.dialog.showModal();
|
||||
registryUI.addTag.dialog.open();
|
||||
};
|
||||
registryUI.addTag.add = function () {
|
||||
if (registryUI.addTag.addServer.value && registryUI.addTag.addServer.value.length > 0) {
|
||||
registryUI.addServer(registryUI.addTag.addServer.value);
|
||||
if (registryUI.addTag.dialog.getAddServer().length > 0) {
|
||||
registryUI.addServer(registryUI.addTag.dialog.getAddServer());
|
||||
}
|
||||
registryUI.addTag.addServer.value = '';
|
||||
rg.router.go('home');
|
||||
registryUI.addTag.dialog.close();
|
||||
registryUI.home();
|
||||
registryUI.addTag.close();
|
||||
};
|
||||
registryUI.addTag.close = function () {
|
||||
registryUI.addTag.addServer.value = '';
|
||||
registryUI.addTag.dialog.tags['material-input'].value = '';
|
||||
registryUI.addTag.dialog.close();
|
||||
};
|
||||
registryUI.addTag.update();
|
||||
</script>
|
||||
</add>
|
||||
|
||||
175
src/tags/app.tag
175
src/tags/app.tag
@@ -15,29 +15,160 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<app>
|
||||
<catalog if="{!rg.router.current || rg.router.current.name == 'home'}"></catalog>
|
||||
<taglist if="{rg.router.current.name == 'taglist'}"></taglist>
|
||||
<header>
|
||||
<material-navbar>
|
||||
<div class="logo">Docker Registry UI</div>
|
||||
<menu></menu>
|
||||
</material-navbar>
|
||||
</header>
|
||||
<main>
|
||||
<catalog if="{route.routeName == 'home'}"></catalog>
|
||||
<taglist if="{route.routeName == 'taglist'}"></taglist>
|
||||
<change></change>
|
||||
<add></add>
|
||||
<remove></remove>
|
||||
<material-snackbar></material-snackbar>
|
||||
</main>
|
||||
<footer>
|
||||
<material-footer>
|
||||
<a class="material-footer-logo" href="https://joxit.github.io/docker-registry-ui/">Docker Registry UI %%GULP_INJECT_VERSION%%</a>
|
||||
<ul class="material-footer-link-list">
|
||||
<li>
|
||||
<a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/Joxit/docker-registry-ui/blob/master/LICENSE">Privacy & Terms</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</material-footer>
|
||||
</footer>
|
||||
<script>
|
||||
|
||||
this.mixin('rg.router');
|
||||
this.router.add({name: 'home', url: ''});
|
||||
this.router.add({name: 'taglist', url: '/taglist/:repository/:image'});
|
||||
this.router.on('go', state => {
|
||||
switch (state.name) {
|
||||
case 'taglist':
|
||||
if (registryUI.taglist.display) {
|
||||
registryUI.taglist.loadend = false;
|
||||
registryUI.taglist.display();
|
||||
}
|
||||
break;
|
||||
case 'home':
|
||||
if (registryUI.catalog.display) {
|
||||
registryUI.catalog.loadend = false;
|
||||
registryUI.catalog.display();
|
||||
}
|
||||
break;
|
||||
registryUI.appTag = this;
|
||||
route.base('#!')
|
||||
route('', function() {
|
||||
route.routeName = 'home';
|
||||
if (registryUI.catalog.display) {
|
||||
registryUI.catalog.loadend = false;
|
||||
registryUI.catalog.display();
|
||||
}
|
||||
})
|
||||
this.router.start();
|
||||
registryUI.appTag.update();
|
||||
});
|
||||
route('/taglist/*', function(image) {
|
||||
route.routeName = 'taglist';
|
||||
registryUI.taglist.name = image
|
||||
if (registryUI.taglist.display) {
|
||||
registryUI.taglist.loadend = false;
|
||||
registryUI.taglist.display();
|
||||
}
|
||||
registryUI.appTag.update();
|
||||
});
|
||||
registryUI.home = function() {
|
||||
if(route.routeName == 'home') {
|
||||
registryUI.catalog.display();
|
||||
} else {
|
||||
route('');
|
||||
}
|
||||
};
|
||||
registryUI.snackbar = function (message, isError) {
|
||||
registryUI.appTag.tags['material-snackbar'].addToast({'message': message, 'isError': isError}, 15000);
|
||||
};
|
||||
registryUI.errorSnackbar = function (message) {
|
||||
return registryUI.snackbar(message, true);
|
||||
}
|
||||
registryUI.cleanName = function() {
|
||||
var url = (registryUI.url() && registryUI.url().length > 0 && registryUI.url()) || window.location.host;
|
||||
if (url) {
|
||||
return url.startsWith('http') ? url.replace(/https?:\/\//, '') : url;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
route.parser(null, function(path, filter) {
|
||||
const f = filter
|
||||
.replace(/\?/g, '\\?')
|
||||
.replace(/\*/g, '([^?#]+?)')
|
||||
.replace(/\.\./, '.*')
|
||||
const re = new RegExp('^' + f + '$')
|
||||
const args = path.match(re)
|
||||
if (args) return args.slice(1)
|
||||
});
|
||||
|
||||
registryUI.isDigit = function(char) {
|
||||
return char >= '0' && char <= '9';
|
||||
}
|
||||
|
||||
registryUI.DockerImage = function (name, tag) {
|
||||
this.name = name;
|
||||
this.tag = tag;
|
||||
riot.observable(this);
|
||||
this.on('get-size', function() {
|
||||
if (this.size !== undefined) {
|
||||
return this.trigger('size', this.size);
|
||||
}
|
||||
return this.fillInfo();
|
||||
});
|
||||
this.on('get-sha256', function() {
|
||||
if (this.size !== undefined) {
|
||||
return this.trigger('sha256', this.sha256);
|
||||
}
|
||||
return this.fillInfo();
|
||||
});
|
||||
};
|
||||
|
||||
registryUI.DockerImage._tagReduce = function (acc, e) {
|
||||
if (acc.length > 0 && registryUI.isDigit(acc[acc.length - 1].charAt(0)) == registryUI.isDigit(e)) {
|
||||
acc[acc.length - 1] += e;
|
||||
} else {
|
||||
acc.push(e);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
registryUI.DockerImage.compare = function(e1, e2) {
|
||||
var tag1 = e1.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
|
||||
var tag2 = e2.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
|
||||
|
||||
for (var i = 0; i < tag1.length && i < tag2.length; i++) {
|
||||
var compare = tag1[i].localeCompare(tag2[i]);
|
||||
if (registryUI.isDigit(tag1[i].charAt(0)) && registryUI.isDigit(tag2[i].charAt(0))) {
|
||||
var diff = tag1[i] - tag2[i];
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
} else if (compare != 0) {
|
||||
return compare;
|
||||
}
|
||||
}
|
||||
return e1.tag.length - e2.tag.length;
|
||||
};
|
||||
|
||||
registryUI.DockerImage.prototype.fillInfo = function() {
|
||||
if (this._fillInfoWaiting) {
|
||||
return;
|
||||
}
|
||||
this._fillInfoWaiting = true;
|
||||
var oReq = new Http();
|
||||
var self = this;
|
||||
oReq.addEventListener('loadend', function () {
|
||||
if (this.status == 200 || this.status == 202) {
|
||||
var response = JSON.parse(this.responseText);
|
||||
self.size = response.layers.reduce(function (acc, e) {
|
||||
return acc + e.size;
|
||||
}, 0);
|
||||
self.sha256 = response.config.digest;
|
||||
self.trigger('size', self.size);
|
||||
self.trigger('sha256', self.sha256);
|
||||
} else if (this.status == 404) {
|
||||
registryUI.errorSnackbar('Manifest for ' + self.name + ':' + self.tag + ' not found');
|
||||
} else {
|
||||
registryUI.snackbar(this.responseText);
|
||||
}
|
||||
});
|
||||
oReq.open('GET', registryUI.url() + '/v2/' + self.name + '/manifests/' + self.tag);
|
||||
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
|
||||
oReq.send();
|
||||
}
|
||||
route.start(true);
|
||||
</script>
|
||||
</app>
|
||||
</app>
|
||||
@@ -16,75 +16,54 @@
|
||||
-->
|
||||
<catalog>
|
||||
<!-- Begin of tag -->
|
||||
<div id="catalog-tag" class="catalog">
|
||||
<div class="section-centerd mdl-card mdl-shadow--2dp mdl-cell--6-col">
|
||||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">Repositories of { registryUI.url() }</h2>
|
||||
</div>
|
||||
<div id="catalog-spinner" hide="{ registryUI.catalog.loadend }" class="mdl-spinner mdl-js-spinner is-active section-centerd"></div>
|
||||
<ul class="mdl-list" show="{ registryUI.catalog.loadend }">
|
||||
<li class="mdl-list__item mdl-menu__item" style="opacity: 1;" each="{ item in registryUI.catalog.repositories }" onclick="registryUI.catalog.go('{item}');">
|
||||
<span class="mdl-list__item-primary-content">
|
||||
<i class="material-icons mdl-list__item-icon">send</i>
|
||||
{ item }
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<material-card ref="catalog-tag" class="catalog">
|
||||
<div class="material-card-title-action">
|
||||
<h2>Repositories of { registryUI.name() }</h2>
|
||||
</div>
|
||||
<div id="error-snackbar" aria-live="assertive" aria-atomic="true" aria-relevant="text" class="mdl-js-snackbar mdl-snackbar">
|
||||
<div class="mdl-snackbar__text"></div>
|
||||
<button class="mdl-snackbar__action" type="button"></button>
|
||||
<div hide="{ registryUI.catalog.loadend }" class="spinner-wrapper">
|
||||
<material-spinner></material-spinner>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list highlight" show="{ registryUI.catalog.loadend }">
|
||||
<li each="{ item in registryUI.catalog.repositories }" onclick="registryUI.catalog.go('{item}');">
|
||||
<span>
|
||||
<i class="material-icons">send</i>
|
||||
{ item }
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</material-card>
|
||||
|
||||
<script>
|
||||
registryUI.catalog.instance = this;
|
||||
this.mixin('rg.router');
|
||||
registryUI.catalog.display = function () {
|
||||
registryUI.catalog.repositories = [];
|
||||
var oReq = new Http();
|
||||
registryUI.catalog.createSnackbar = function (msg) {
|
||||
var snackbar = document.querySelector('#error-snackbar');
|
||||
registryUI.catalog.error = msg;
|
||||
var data = {
|
||||
message: registryUI.catalog.error,
|
||||
timeout: 100000,
|
||||
actionHandler: function () {
|
||||
snackbar.classList.remove('mdl-snackbar--active');
|
||||
},
|
||||
actionText: 'Undo'
|
||||
};
|
||||
snackbar.MaterialSnackbar.showSnackbar(data);
|
||||
};
|
||||
oReq.addEventListener('load', function () {
|
||||
registryUI.catalog.repositories = [];
|
||||
if (this.status == 200) {
|
||||
registryUI.catalog.repositories = JSON.parse(this.responseText).repositories || [];
|
||||
registryUI.catalog.repositories.sort();
|
||||
} else if (this.status == 404) {
|
||||
registryUI.catalog.createSnackbar('Server not found');
|
||||
registryUI.snackbar('Server not found', true);
|
||||
} else {
|
||||
registryUI.catalog.createSnackbar(this.responseText);
|
||||
registryUI.snackbar(this.responseText);
|
||||
}
|
||||
});
|
||||
oReq.addEventListener('error', function () {
|
||||
registryUI.catalog.createSnackbar('An error occured');
|
||||
registryUI.snackbar(this.getErrorMessage(), true);
|
||||
registryUI.catalog.repositories = [];
|
||||
});
|
||||
oReq.addEventListener('loadend', function () {
|
||||
registryUI.catalog.loadend = true;
|
||||
registryUI.catalog.instance.update();
|
||||
});
|
||||
oReq.open('GET', registryUI.url() + '/v2/_catalog');
|
||||
oReq.open('GET', registryUI.url() + '/v2/_catalog?n=100000');
|
||||
oReq.send();
|
||||
};
|
||||
this.on('updated', function () {
|
||||
componentHandler.upgradeElements(this['catalog-tag']);
|
||||
});
|
||||
registryUI.catalog.go = function (image) {
|
||||
rg.router.go('taglist', {
|
||||
repository: image.split('/')[0],
|
||||
image: image.split('/')[1]
|
||||
});
|
||||
route('taglist/' + image);
|
||||
};
|
||||
registryUI.catalog.display();
|
||||
</script>
|
||||
<!-- End of tag -->
|
||||
</catalog>
|
||||
</catalog>
|
||||
|
||||
@@ -15,52 +15,47 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<change>
|
||||
<dialog id="change-server-dialog" class="mdl-dialog">
|
||||
<h4 class="mdl-dialog__title">Change your Server ?</h4>
|
||||
<div class="mdl-dialog__content">
|
||||
<div class="mdl-textfield mdl-js-textfield">
|
||||
<select class="mdl-textfield__input mdl-textfield__select" name="server-list" id="server-list">
|
||||
<material-popup>
|
||||
<div class="material-popup-title">Change your Server ?</div>
|
||||
<div class="material-popup-content">
|
||||
<div class="select-padding">
|
||||
<select class="mdl-textfield__input mdl-textfield__select" name="server-list" ref="server-list">
|
||||
<option each="{ url in registryUI.getRegistryServer() }" value={url}>{url}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdl-dialog__actions">
|
||||
<button type="button" class="mdl-button change" onClick="registryUI.changeTag.change();">Change</button>
|
||||
<button type="button" class="mdl-button close" onClick="registryUI.changeTag.close();">Cancel</button>
|
||||
<div class="material-popup-action">
|
||||
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.changeTag.change();">Change</material-button>
|
||||
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.changeTag.close();">Cancel</material-button>
|
||||
</div>
|
||||
</dialog>
|
||||
</material-popup>
|
||||
<script type="text/javascript">
|
||||
registryUI.changeTag = registryUI.changeTag || {};
|
||||
registryUI.changeTag.update = this.update;
|
||||
this.on('updated', function () {
|
||||
componentHandler.upgradeElements(this['change-server-dialog']);
|
||||
registryUI.changeTag.dialog = registryUI.changeTag.dialog || document.querySelector('#change-server-dialog');
|
||||
registryUI.changeTag.serverList = registryUI.changeTag.serverList || registryUI.changeTag.dialog.querySelector('#server-list');
|
||||
if (!registryUI.changeTag.dialog.showModal) {
|
||||
dialogPolyfill.registerDialog(registryUI.changeTag.dialog);
|
||||
}
|
||||
this['server-list'].onkeyup = function (e) {
|
||||
// if keyCode is Enter
|
||||
if (e.keyCode == 13) {
|
||||
registryUI.changeTag.change();
|
||||
}
|
||||
this.one('mount', function () {
|
||||
registryUI.changeTag.dialog = this.tags['material-popup'];
|
||||
registryUI.changeTag.dialog.getServerUrl = function () {
|
||||
return this.refs['server-list']
|
||||
? this.refs['server-list'].value
|
||||
: '';
|
||||
};
|
||||
registryUI.changeTag.dialog.on('updated', function () {
|
||||
if (this.refs['server-list']) {
|
||||
this.refs['server-list'].value = registryUI.url();
|
||||
}
|
||||
});
|
||||
});
|
||||
registryUI.changeTag.show = function () {
|
||||
registryUI.changeTag.update();
|
||||
registryUI.changeTag.serverList.value = registryUI.url();;
|
||||
registryUI.changeTag.dialog.showModal();
|
||||
registryUI.changeTag.dialog.open();
|
||||
};
|
||||
registryUI.changeTag.change = function () {
|
||||
if (registryUI.changeTag.serverList.value && registryUI.changeTag.serverList.value.length > 0) {
|
||||
registryUI.changeServer(registryUI.changeTag.serverList.value);
|
||||
if (registryUI.changeTag.dialog.getServerUrl().length > 0) {
|
||||
registryUI.changeServer(registryUI.changeTag.dialog.getServerUrl());
|
||||
}
|
||||
registryUI.changeTag.serverList.value = '';
|
||||
rg.router.go('home');
|
||||
registryUI.home();
|
||||
registryUI.changeTag.dialog.close();
|
||||
};
|
||||
registryUI.changeTag.close = function () {
|
||||
registryUI.changeTag.dialog.close();
|
||||
};
|
||||
</script>
|
||||
</change>
|
||||
</change>
|
||||
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-2018 Jones Magloire @Joxit
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<copy-to-clipboard>
|
||||
<input ref="input" style="display: none; width: 1px; height: 1px;" value="{ this.dockerCmd }">
|
||||
<a onclick="{ this.copy }" title="Copy pull command.">
|
||||
<i class="material-icons">content_copy</i>
|
||||
</a>
|
||||
<script type="text/javascript">
|
||||
this.dockerCmd = 'docker pull ' + registryUI.cleanName() + '/' + opts.image.name + ':' + opts.image.tag;
|
||||
this.copy = function () {
|
||||
var copyText = this.refs['input'];
|
||||
copyText.style.display = 'block';
|
||||
copyText.select();
|
||||
document.execCommand('copy');
|
||||
copyText.style.display = 'none';
|
||||
|
||||
registryUI.snackbar('`' + this.dockerCmd + '` has been copied to clipboard.')
|
||||
};
|
||||
</script>
|
||||
</copy-to-clipboard>
|
||||
37
src/tags/image-size.tag
Normal file
37
src/tags/image-size.tag
Normal file
@@ -0,0 +1,37 @@
|
||||
<!--
|
||||
Copyright (C) 2018 Jones Magloire @Joxit
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<image-size>
|
||||
<div title="Compressed size of your image.">{ this.bytesToSize(this.size) }</div>
|
||||
<script type="text/javascript">
|
||||
var self = this;
|
||||
this.bytesToSize = function (bytes) {
|
||||
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes == undefined || isNaN(bytes)) {
|
||||
return '?';
|
||||
} else if (bytes == 0) {
|
||||
return '0 Byte';
|
||||
}
|
||||
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
return Math.ceil(bytes / Math.pow(1024, i)) + ' ' + sizes[i];
|
||||
};
|
||||
opts.image.on('size', function(size) {
|
||||
self.size = size;
|
||||
self.update();
|
||||
});
|
||||
opts.image.trigger('get-size');
|
||||
</script>
|
||||
</image-size>
|
||||
27
src/tags/image-tag.tag
Normal file
27
src/tags/image-tag.tag
Normal file
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2018 Jones Magloire @Joxit
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<image-tag>
|
||||
<div title="{ this.sha256 }">{ opts.image.tag }</div>
|
||||
<script type="text/javascript">
|
||||
var self = this;
|
||||
opts.image.on('sha256', function(sha256) {
|
||||
self.sha256 = sha256.substring(0, 19);
|
||||
self.update();
|
||||
});
|
||||
opts.image.trigger('get-sha256');
|
||||
</script>
|
||||
</image-tag>
|
||||
@@ -15,23 +15,31 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<menu>
|
||||
<div id="card-menu" class="mdl-card__menu">
|
||||
<button id="registry-menu" name="registry-menu" class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect" for="registry-menu">
|
||||
<li class="mdl-menu__item" onclick="registryUI.addTag.show();">Add URL</li>
|
||||
<li class="mdl-menu__item" onclick="registryUI.changeTag.show();">Change URL</li>
|
||||
<li class="mdl-menu__item" onclick="registryUI.removeTag.show();">Remove URL</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<material-button id="menu-control-button" onclick="registryUI.menuTag.toggle();" waves-center="true" rounded="true" waves-opacity="0.6" waves-duration="600">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</material-button>
|
||||
<material-dropdown id="menu-control-dropdown">
|
||||
<p onclick="registryUI.addTag.show(); registryUI.menuTag.close();">Add URL</p>
|
||||
<p onclick="registryUI.changeTag.show(); registryUI.menuTag.close();">Change URL</p>
|
||||
<p onclick="registryUI.removeTag.show(); registryUI.menuTag.close();">Remove URL</p>
|
||||
</material-dropdown>
|
||||
<div class="overlay" onclick="registryUI.menuTag.close();" show="{ registryUI.menuTag.isOpen && registryUI.menuTag.isOpen() }"></div>
|
||||
<script type="text/javascript">
|
||||
registryUI.menuTag = registryUI.menuTag || {};
|
||||
registryUI.menuTag.update = this.update;
|
||||
this.on('updated', function () {
|
||||
componentHandler.upgradeElements(this['card-menu']);
|
||||
this.one('mount', function(args) {
|
||||
var self = this;
|
||||
registryUI.menuTag.close = function() {
|
||||
self.tags['material-dropdown'].close();
|
||||
self.update();
|
||||
}
|
||||
registryUI.menuTag.isOpen = function() {
|
||||
return self.tags['material-dropdown'].opened;
|
||||
}
|
||||
registryUI.menuTag.toggle = function() {
|
||||
self.tags['material-dropdown'].opened ? self.tags['material-dropdown'].close() : self.tags['material-dropdown'].open();
|
||||
self.update();
|
||||
};
|
||||
});
|
||||
registryUI.menuTag.update();
|
||||
</script>
|
||||
</menu>
|
||||
|
||||
@@ -15,44 +15,43 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<remove-image>
|
||||
<a href="#" onclick="registryUI.removeImage.remove('{ opts.name }', '{ opts.tag }')">
|
||||
<i class="material-icons mdl-list__item-icon">delete</i>
|
||||
<a href="#" title="This will delete the image." onclick="registryUI.removeImage.remove('{ opts.image.name }', '{ opts.image.tag }')">
|
||||
<i class="material-icons">delete</i>
|
||||
</a>
|
||||
<script type="text/javascript">
|
||||
registryUI.removeImage = registryUI.removeImage || {}
|
||||
registryUI.removeImage.update = this.update;
|
||||
|
||||
registryUI.removeImage = registryUI.removeImage || {};
|
||||
|
||||
registryUI.removeImage.remove = function (name, tag) {
|
||||
var oReq = new Http();
|
||||
oReq.addEventListener('load', function () {
|
||||
oReq.addEventListener('loadend', function () {
|
||||
registryUI.taglist.refresh();
|
||||
if (this.status == 200) {
|
||||
if (!this.getAllResponseHeaders().includes('Docker-Content-Digest')) {
|
||||
registryUI.taglist.createSnackbar('You need tu add Access-Control-Expose-Headers: [\'Docker-Content-Digest\'] in your server configuration.');
|
||||
if (!this.hasHeader('Docker-Content-Digest')) {
|
||||
registryUI.errorSnackbar('You need to add Access-Control-Expose-Headers: [\'Docker-Content-Digest\'] in your server configuration.');
|
||||
return;
|
||||
}
|
||||
var digest = this.getResponseHeader('Docker-Content-Digest');
|
||||
var oReq = new Http();
|
||||
oReq.addEventListener('load', function () {
|
||||
oReq.addEventListener('loadend', function () {
|
||||
if (this.status == 200 || this.status == 202) {
|
||||
registryUI.taglist.refresh();
|
||||
registryUI.taglist.createSnackbar('Deleting ' + name + ':' + tag + ' image. Run `registry garbage-collect config.yml` on your registry');
|
||||
registryUI.snackbar('Deleting ' + name + ':' + tag + ' image. Run `registry garbage-collect config.yml` on your registry');
|
||||
} else if (this.status == 404) {
|
||||
registryUI.taglist.createSnackbar('Digest not found');
|
||||
registryUI.errorSnackbar('Digest not found');
|
||||
} else {
|
||||
registryUI.taglist.createSnackbar(this.responseText);
|
||||
registryUI.snackbar(this.responseText);
|
||||
}
|
||||
});
|
||||
oReq.open('DELETE', registryUI.url() + '/v2/' + name + '/manifests/' + digest);
|
||||
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
|
||||
oReq.addEventListener('error', function () {
|
||||
registryUI.taglist.createSnackbar('An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: [\'DELETE\'].');
|
||||
registryUI.errorSnackbar('An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: [\'DELETE\'].');
|
||||
});
|
||||
oReq.send();
|
||||
} else if (this.status == 404) {
|
||||
registryUI.taglist.createSnackbar('Manifest for' + name + ':' + tag + 'not found');
|
||||
registryUI.errorSnackbar('Manifest for ' + name + ':' + tag + ' not found');
|
||||
} else {
|
||||
registryUI.taglist.createSnackbar(this.responseText);
|
||||
registryUI.snackbar(this.responseText);
|
||||
}
|
||||
});
|
||||
oReq.open('HEAD', registryUI.url() + '/v2/' + name + '/manifests/' + tag);
|
||||
|
||||
@@ -15,33 +15,32 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<remove>
|
||||
<dialog id="remove-server-dialog" class="mdl-dialog">
|
||||
<h4 class="mdl-dialog__title">Remove your Registry Server ?</h4>
|
||||
<div class="mdl-dialog__content">
|
||||
<div class="mdl-textfield mdl-js-textfield">
|
||||
<ul class="mdl-list">
|
||||
<li class="mdl-list__item" each="{ url in registryUI.getRegistryServer() }">
|
||||
<span class="mdl-list__item-primary-content">
|
||||
<a href="#" onClick="registryUI.removeTag.removeUrl('{url}');">
|
||||
<i class="material-icons mdl-list__item-icon">delete</i>
|
||||
</a>
|
||||
<span class="url">{url}</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdl-dialog__actions">
|
||||
<button type="button" class="mdl-button close" onClick="registryUI.removeTag.close();">Close</button>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<material-popup>
|
||||
<div class="material-popup-title">Remove your Registry Server ?</div>
|
||||
<div class="material-popup-content">
|
||||
<ul class="list">
|
||||
<li each="{ url in registryUI.getRegistryServer() }">
|
||||
<span>
|
||||
<a href="#" onClick="registryUI.removeTag.removeUrl('{url}');">
|
||||
<i class="material-icons">delete</i>
|
||||
</a>
|
||||
<span class="url">{ url }</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="material-popup-action">
|
||||
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.removeTag.close();">Close</material-button>
|
||||
</div>
|
||||
</material-popup>
|
||||
<script type="text/javascript">
|
||||
registryUI.removeTag = registryUI.removeTag || {}
|
||||
registryUI.removeTag.update = this.update;
|
||||
|
||||
registryUI.removeTag.removeUrl = function (url) {
|
||||
registryUI.removeServer(url);
|
||||
registryUI.removeTag.update();
|
||||
registryUI.removeTag.close();
|
||||
};
|
||||
|
||||
registryUI.removeTag.close = function () {
|
||||
@@ -50,15 +49,11 @@
|
||||
};
|
||||
|
||||
registryUI.removeTag.show = function () {
|
||||
registryUI.removeTag.update();
|
||||
registryUI.removeTag.dialog.showModal();
|
||||
registryUI.removeTag.dialog.open();
|
||||
};
|
||||
|
||||
this.on('updated', function () {
|
||||
registryUI.removeTag.dialog = this['remove-server-dialog'];
|
||||
if (!registryUI.removeTag.dialog.showModal) {
|
||||
dialogPolyfill.registerDialog(registryUI.removeTag.dialog);
|
||||
}
|
||||
this.one('mount', function () {
|
||||
registryUI.removeTag.dialog = this.tags['material-popup'];
|
||||
});
|
||||
</script>
|
||||
</remove>
|
||||
|
||||
@@ -16,106 +16,89 @@
|
||||
-->
|
||||
<taglist>
|
||||
<!-- Begin of tag -->
|
||||
<div id="taglist-tag" class="taglist">
|
||||
<div class="section-centerd mdl-card mdl-shadow--2dp mdl-cell--6-col">
|
||||
<div class="mdl-card__title">
|
||||
<a href="#" onclick="registryUI.taglist.back();">
|
||||
<i class="material-icons mdl-list__item-icon">arrow_back</i>
|
||||
</a>
|
||||
<h2 class="mdl-card__title-text">Tags of { registryUI.url() + '/' + registryUI.taglist.name }</h2>
|
||||
</div>
|
||||
<div id="taglist-spinner" hide="{ registryUI.taglist.loadend }" class="mdl-spinner mdl-js-spinner section-centerd is-active"></div>
|
||||
<table class="mdl-data-table mdl-js-data-table full-table" show="{ registryUI.taglist.loadend }" style="border: none;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="mdl-data-table__cell--non-numeric">Repository</th>
|
||||
<th class="{ registryUI.taglist.asc ? 'mdl-data-table__header--sorted-ascending' : 'mdl-data-table__header--sorted-descending' }" onclick="registryUI.taglist.reverse();">Tag</th>
|
||||
<th show="{ registryUI.isImageRemoveActivated }" ></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr each="{ item in registryUI.taglist.tags }">
|
||||
<td class="mdl-data-table__cell--non-numeric">{ registryUI.taglist.name }</td>
|
||||
<td>{ item }</td>
|
||||
<td show="{ registryUI.isImageRemoveActivated }" >
|
||||
<remove-image name={ registryUI.taglist.name } tag={ item } />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<material-card ref="taglist-tag" class="taglist">
|
||||
<div class="material-card-title-action">
|
||||
<a href="#!" onclick="registryUI.home();">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</a>
|
||||
<h2>Tags of { registryUI.name() + '/' + registryUI.taglist.name }</h2>
|
||||
</div>
|
||||
<div id="error-snackbar" aria-live="assertive" aria-atomic="true" aria-relevant="text" class="mdl-js-snackbar mdl-snackbar">
|
||||
<div class="mdl-snackbar__text"></div>
|
||||
<button class="mdl-snackbar__action" type="button"></button>
|
||||
<div hide="{ registryUI.taglist.loadend }" class="spinner-wrapper">
|
||||
<material-spinner></material-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table show="{ registryUI.taglist.loadend }" style="border: none;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="material-card-th-left">Repository</th>
|
||||
<th></th>
|
||||
<th>Size</th>
|
||||
<th class="{ registryUI.taglist.asc ? 'material-card-th-sorted-ascending' : 'material-card-th-sorted-descending' }" onclick="registryUI.taglist.reverse();">Tag</th>
|
||||
<th show="{ registryUI.isImageRemoveActivated }"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr each="{ image in registryUI.taglist.tags }">
|
||||
<td class="material-card-th-left">{ image.name }</td>
|
||||
<td class="copy-to-clipboard">
|
||||
<copy-to-clipboard image={ image }/>
|
||||
</td>
|
||||
<td><image-size image="{ image }" /></td>
|
||||
<td><image-tag image="{ image }" /></td>
|
||||
<td show="{ registryUI.isImageRemoveActivated }">
|
||||
<remove-image image={ image }/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</material-card>
|
||||
<script>
|
||||
registryUI.taglist.instance = this;
|
||||
registryUI.taglist.display = function () {
|
||||
if (rg.router.current && rg.router.current.name == 'taglist') {
|
||||
name = rg.router.current.params.repository + (rg.router.current.params.image
|
||||
? '/' + rg.router.current.params.image
|
||||
: '');
|
||||
registryUI.taglist.tags = [];
|
||||
if (route.routeName == 'taglist') {
|
||||
var oReq = new Http();
|
||||
registryUI.taglist.name = name;
|
||||
registryUI.taglist.instance.update();
|
||||
registryUI.taglist.createSnackbar = function (msg) {
|
||||
var snackbar = document.querySelector('#error-snackbar');
|
||||
registryUI.taglist.error = msg;
|
||||
var data = {
|
||||
message: registryUI.taglist.error,
|
||||
timeout: 100000,
|
||||
actionHandler: function () {
|
||||
snackbar.classList.remove('mdl-snackbar--active');
|
||||
},
|
||||
actionText: 'Undo'
|
||||
};
|
||||
snackbar.MaterialSnackbar.showSnackbar(data);
|
||||
};
|
||||
oReq.addEventListener('load', function () {
|
||||
registryUI.taglist.tags = [];
|
||||
if (this.status == 200) {
|
||||
registryUI.taglist.tags = JSON.parse(this.responseText).tags || [];
|
||||
registryUI.taglist.tags.sort();
|
||||
registryUI.taglist.tags = registryUI.taglist.tags.map(function(tag) {
|
||||
return new registryUI.DockerImage(registryUI.taglist.name, tag);
|
||||
}).sort(registryUI.DockerImage.compare);
|
||||
} else if (this.status == 404) {
|
||||
registryUI.taglist.createSnackbar('Server not found');
|
||||
registryUI.snackbar('Server not found', true);
|
||||
} else {
|
||||
registryUI.taglist.createSnackbar(this.responseText);
|
||||
registryUI.snackbar(this.responseText, true);
|
||||
}
|
||||
});
|
||||
oReq.addEventListener('error', function () {
|
||||
registryUI.taglist.createSnackbar('An error occured');
|
||||
registryUI.snackbar(this.getErrorMessage(), true);
|
||||
registryUI.taglist.tags = [];
|
||||
});
|
||||
oReq.addEventListener('loadend', function () {
|
||||
registryUI.taglist.loadend = true;
|
||||
registryUI.taglist.instance.update();
|
||||
});
|
||||
oReq.open('GET', registryUI.url() + '/v2/' + name + '/tags/list');
|
||||
oReq.open('GET', registryUI.url() + '/v2/' + registryUI.taglist.name + '/tags/list');
|
||||
oReq.send();
|
||||
registryUI.taglist.asc = true;
|
||||
}
|
||||
};
|
||||
registryUI.taglist.display();
|
||||
registryUI.taglist.instance.update();
|
||||
this.on('updated', function () {
|
||||
componentHandler.upgradeElements(this['taglist-tag']);
|
||||
});
|
||||
|
||||
registryUI.taglist.reverse = function () {
|
||||
if (registryUI.taglist.asc) {
|
||||
registryUI.taglist.tags.reverse();
|
||||
registryUI.taglist.asc = false;
|
||||
} else {
|
||||
registryUI.taglist.tags.sort();
|
||||
registryUI.taglist.tags.sort(registryUI.DockerImage.compare);
|
||||
registryUI.taglist.asc = true;
|
||||
}
|
||||
registryUI.taglist.instance.update();
|
||||
};
|
||||
registryUI.taglist.back = function () {
|
||||
rg.router.go('home');
|
||||
};
|
||||
registryUI.taglist.refresh = function () {
|
||||
rg.router.go(rg.router.current.name, rg.router.current.params);
|
||||
route(registryUI.taglist.name);
|
||||
};
|
||||
</script>
|
||||
<!-- End of tag -->
|
||||
|
||||
26
static.dockerfile
Normal file
26
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:alpine
|
||||
|
||||
LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY dist/ /usr/share/nginx/html/
|
||||
COPY dist/scripts/docker-registry-ui-static.js /usr/share/nginx/html/scripts/docker-registry-ui.js
|
||||
COPY bin/entrypoint /bin
|
||||
|
||||
ENTRYPOINT entrypoint
|
||||
Reference in New Issue
Block a user