mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2026-02-15 21:09:51 +00:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b7bd6cfb8 | ||
|
|
f56cd055a2 | ||
|
|
79203787bd | ||
|
|
32d895a09d | ||
|
|
bc26eca908 | ||
|
|
e301dda408 | ||
|
|
ce93a4da92 | ||
|
|
7296c2e727 | ||
|
|
94fcd53792 | ||
|
|
95558fe75a | ||
|
|
44f3242b85 | ||
|
|
9b05503e77 | ||
|
|
980c5ddaa9 | ||
|
|
2eeae54bca | ||
|
|
f7fe27c07e | ||
|
|
48aba129ce | ||
|
|
a2e81ac12c | ||
|
|
a48ce7ef75 | ||
|
|
04f4c204d8 | ||
|
|
41d71a1991 | ||
|
|
b3fbaff578 | ||
|
|
5e2ead0c2a | ||
|
|
70d5ae2601 | ||
|
|
3b0e63ff12 | ||
|
|
b6144ae13f | ||
|
|
65bbac3453 | ||
|
|
d380a767af | ||
|
|
36c44b19a3 | ||
|
|
28310f9804 | ||
|
|
6296a192d6 | ||
|
|
a0c88d06f0 | ||
|
|
eec736d4e8 | ||
|
|
9e99b08b82 | ||
|
|
0f805daafa | ||
|
|
8bd1f31c9c | ||
|
|
7777ff28df | ||
|
|
4fee7b44d3 | ||
|
|
79960ea52d | ||
|
|
7716f8b44a | ||
|
|
0ac7a151d9 | ||
|
|
1321d9b573 | ||
|
|
ef149bf1cc | ||
|
|
e5a406a6ba | ||
|
|
dbb746981a | ||
|
|
d7a19734ce | ||
|
|
92fc37adb4 | ||
|
|
02210e0943 | ||
|
|
0199f87087 | ||
|
|
3399030e4e | ||
|
|
660a938d6e | ||
|
|
7356591292 | ||
|
|
32d0df1af9 | ||
|
|
5c1cb93a1c | ||
|
|
9d97c30914 | ||
|
|
cec469ab67 | ||
|
|
34200c1114 | ||
|
|
67c6cb1bee | ||
|
|
021cddfef7 | ||
|
|
c01b440ff2 | ||
|
|
63c310181b | ||
|
|
ab37bcfcef | ||
|
|
8e539be6ba | ||
|
|
5b0ffc8eab | ||
|
|
188cb80463 | ||
|
|
f1bff47468 | ||
|
|
df508cdef0 | ||
|
|
eadaaadadd |
61
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
61
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Hi, I use this docker registry UI and I have an issue...
|
||||
|
||||
## Bug description
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
## How to Reproduce
|
||||
|
||||
For UI bug, steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
For service bug, steps to reproduce the behavior:
|
||||
<!-- Remove your credentials or anonymize them -->
|
||||
My docker-compose file
|
||||
```yml
|
||||
|
||||
```
|
||||
|
||||
My private docker registry configuration
|
||||
```yml
|
||||
|
||||
```
|
||||
|
||||
## Expected behavior
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
## Screenshots
|
||||
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
## System information
|
||||
|
||||
- OS: [e.g. Debian 10, Windows, Android 9...]
|
||||
<!-- Browser is only for UI bugs -->
|
||||
- Browser:
|
||||
- Name: [e.g. Chrome, Firefox...]
|
||||
- Version: [e.g. 22]
|
||||
- Docker registry UI:
|
||||
- Version: [e.g. 1.3.0]
|
||||
- Interface variant: [standard or static]
|
||||
- Server: [docker or dist]
|
||||
<!-- Only for Docker and for where the UI is hosted -->
|
||||
- Docker version: [e.g. 19.03]
|
||||
- OS/Arch: [e.g. linux/amd64]
|
||||
- Tools: [e.g. docker-compose, kubernets..]
|
||||
|
||||
## Additional context
|
||||
|
||||
Add any other context about the problem here.
|
||||
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,4 +2,5 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
registry-data
|
||||
.idea
|
||||
.idea
|
||||
_site
|
||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at contact@joxit.dev. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
27
CONTRIBUTORS.md
Normal file
27
CONTRIBUTORS.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Contributors
|
||||
|
||||
## Committers
|
||||
|
||||
- Jones Magloire [@Joxit](https://github.com/Joxit)
|
||||
- Lennart Blom [@lennartblom](https://github.com/lennartblom)
|
||||
- Jakob Ackermann [@das7pad](https://github.com/das7pad)
|
||||
- Olivier Nizet [@onizet](https://github.com/onizet)
|
||||
- Anton Antonov [@syndbg](https://github.com/syndbg)
|
||||
- Bertrand Pechenot [@berpec](https://github.com/berpec)
|
||||
- Sébastien Huss [@sebt3](https://github.com/sebt3)
|
||||
- Vladimir Kozyrev [@fieryvova](https://github.com/fieryvova)
|
||||
- Haibo Jia [@bluethon](https://github.com/bluethon)
|
||||
|
||||
## Because committers are not the only contributors
|
||||
|
||||
- Ewout Prangsma [@ewoutp](https://github.com/ewoutp)
|
||||
- [@soosap](https://github.com/soosap)
|
||||
- Madhukar Mishra [@madhukar93](https://github.com/madhukar93)
|
||||
- Yoan Blanc [@greut](https://github.com/greut)
|
||||
- [@samuelmaier](https://github.com/samuelmaier)
|
||||
- [@sbaloo](https://github.com/sbaloo)
|
||||
- [@rucciva](https://github.com/rucciva)
|
||||
- [@wuyue92tree](https://github.com/wuyue92tree)
|
||||
- Giovanni Toraldo [@gionn](https://github.com/gionn)
|
||||
- [@marcusblake](https://github.com/marcusblake)
|
||||
- Dario [@pidario](https://github.com/pidario)
|
||||
99
README.md
99
README.md
@@ -1,37 +1,70 @@
|
||||
---
|
||||
title: Project Page
|
||||
---
|
||||
|
||||
# Docker Registry UI
|
||||
|
||||

|
||||

|
||||
|
||||
## Overview
|
||||
|
||||
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.
|
||||
This project aims to provide a simple and complete user interface for your private docker registry.
|
||||
You have the choice between two versions, the **standard interface** and the **static interface**.
|
||||
|
||||
In the **standard interface**, there is no default registry, you need to add your own within the UI.
|
||||
You can manage more than one registry server.
|
||||
All registries 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. No configuration is needed when you launch the UI.
|
||||
|
||||
In the **static interface**, it will connect to a single registry and will not change. The configuration is done at the start of the interface, when you use the docker images whose tags contain the `static` keyword.
|
||||
|
||||
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/)
|
||||
## [Project Page](https://joxit.dev/docker-registry-ui), [Live Demo](https://joxit.dev/docker-registry-ui/demo/) [Examples](https://github.com/Joxit/docker-registry-ui/tree/master/examples)
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- List all your repositories/images.
|
||||
- List all tags for a repository/image
|
||||
- Sort the tag list
|
||||
- One interface for many registries
|
||||
- Use a secured docker registry
|
||||
- Share your docker registry with query parameter `url` (e.g. `https://joxit.github.io/docker-registry-ui/demo?url=https://registry.example.com`)
|
||||
- Use `joxit/docker-registry-ui:static` as reverse proxy to your docker registry (This will avoid CORS).
|
||||
- Display image size (see #30)
|
||||
- Add Title when using REGISTRY_URL (see #28)
|
||||
- Alpine and Debian based images with supports for arm32v7 and arm64v8
|
||||
- Copy `docker pull` command to clipbloard
|
||||
- Show sha256 for specific tag (hover image tag)
|
||||
- Display image creation date (see #49)
|
||||
- Display image history (see #58)
|
||||
- Display image/tag count
|
||||
- Image aggregation (see #56)
|
||||
- Customise docker pull command on static registry UI (see #71)
|
||||
- List all tags for a image.
|
||||
- Sort the tag list with number compatibility (see [#46](https://github.com/Joxit/docker-registry-ui/pull/46)).
|
||||
- Use a secured docker registry.
|
||||
- Display image size (see [#30](https://github.com/Joxit/docker-registry-ui/issues/30)).
|
||||
- Multi arch supports, Alpine and Debian based images with supports for arm32v7 and arm64v8.
|
||||
- Copy `docker pull` command to clipboard (see [#42](https://github.com/Joxit/docker-registry-ui/issues/42)).
|
||||
- Show sha256 for specific tag (hover image tag).
|
||||
- Display image creation date (see [#49](https://github.com/Joxit/docker-registry-ui/issues/49))
|
||||
- Display image history (see [#58](https://github.com/Joxit/docker-registry-ui/pull/58) & [#61](https://github.com/Joxit/docker-registry-ui/pull/61)).
|
||||
- Image aggregation (see [#56](https://github.com/Joxit/docker-registry-ui/issues/56)).
|
||||
- Display image/tag count (see [#56 issue comment](https://github.com/Joxit/docker-registry-ui/issues/56#issuecomment-449246524)).
|
||||
- Select multiple tags to delete (see [#29](https://github.com/Joxit/docker-registry-ui/issues/29)).
|
||||
- Select all tags with ALT + Click to delete (see [#80](https://github.com/Joxit/docker-registry-ui/issues/80)).
|
||||
- One interface for many registries **standard interface**.
|
||||
- Share your docker registry with query parameter `url` (e.g. `https://joxit.dev/docker-registry-ui/demo?url=https://registry.example.com`) **standard interface**.
|
||||
- Use `joxit/docker-registry-ui:static` as reverse proxy (with `REGISTRY_URL` environment variable) to your docker registry (This will avoid CORS) **static interface**.
|
||||
- Add Title when using `REGISTRY_URL` (see [#28](https://github.com/Joxit/docker-registry-ui/issues/28)) **static interface**.
|
||||
- Customise docker pull command on static registry UI (see [#71](https://github.com/Joxit/docker-registry-ui/issues/71)) **static interface**.
|
||||
- Add custom header via environment variable and file via `NGINX_PROXY_HEADER_*` (see [#89](https://github.com/Joxit/docker-registry-ui/pull/89)) **static interface**
|
||||
|
||||
## FAQ
|
||||
|
||||
- Why, when I delete all tags of an image, the image is still in the UI ?
|
||||
- This is a limitation of docker registry, the garbage collector don't remove empty images. If you want to delete dangling images, you will need to delete the folder in your registry data. (see [#77](https://github.com/Joxit/docker-registry-ui/issues/77))
|
||||
- Why the image size in the UI is not the same as displayed during `docker images` ?
|
||||
- The UI displays the compressed size of the image and not the extracted size version.
|
||||
- Can I use HTTPS on the UI ?
|
||||
- Yes, put your favourite reverse proxy on the front of the UI. Your reverse proxy will take care of HTTPS connection.
|
||||
- Does the UI support authentication ?
|
||||
- Yes, but it supports only basic auth. It's a simple standalone frontend, it will use your browser window for authentication.
|
||||
- Can I use the UI and docker client with an insecure registry (registry url without https) ?
|
||||
- Yes you can, you must first configure your docker client. (see [#76](https://github.com/Joxit/docker-registry-ui/issues/76))
|
||||
- What does Mixed Content error mean ?
|
||||
- This means you are using a UI with HTTPS and your registry is using HTTP (unsecured). When you are on a HTTPS site, you can't get HTTP content. Upgrade you registry with a HTTPS connection.
|
||||
- Why the default nginx `Host` is set to `$http_host` ?
|
||||
- This fixes the issue [#88](https://github.com/Joxit/docker-registry-ui/issues/88). More about this in [#113](https://github.com/Joxit/docker-registry-ui/issues/113).
|
||||
|
||||
Need more informations ? Try my [examples](https://github.com/Joxit/docker-registry-ui/tree/master/examples) or open an issue.
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -102,15 +135,16 @@ To run the docker and see the website on your 80 port, try this:
|
||||
docker run -d -p 80:80 joxit/docker-registry-ui
|
||||
```
|
||||
|
||||
#### Run the static docker
|
||||
#### Run the static interface
|
||||
|
||||
Some env options are available for use this interface for only one server.
|
||||
|
||||
- `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).
|
||||
- [`URL`](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-standalone): set the static URL to use (You will need CORS configuration). Example: `http://127.0.0.1:5000`. (`Required`)
|
||||
- [`REGISTRY_URL`](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-proxy): 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).
|
||||
- `PULL_URL`: Set a custom url for the docker pull command, this is useful when you use `REGISTRY_URL` and your registry is on a different host (since 1.1.0).
|
||||
- [`NGINX_PROXY_HEADER_*`](https://github.com/Joxit/docker-registry-ui/tree/master/examples/proxy-headers): Set custom headers for your docker registry, usefull when you want to add your credentials. (Can be use only with `REGISTRY_URL`).
|
||||
|
||||
Example with `URL` option.
|
||||
|
||||
@@ -119,7 +153,7 @@ docker run -d -p 80:80 -e URL=http://127.0.0.1:5000 -e DELETE_IMAGES=true joxit/
|
||||
```
|
||||
|
||||
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).
|
||||
Your registry will be accessible here : `http://127.0.0.1/v2`, this will avoid CORS errors (see [#25](https://github.com/Joxit/docker-registry-ui/issues/25#issuecomment-360522487)).
|
||||
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
|
||||
@@ -136,15 +170,15 @@ Your server should be configured to accept CORS.
|
||||
|
||||
If your docker registry does not need credentials, you will need to send this HEADER:
|
||||
|
||||
Access-Control-Allow-Origin: '*'
|
||||
Access-Control-Allow-Origin: ['*']
|
||||
|
||||
If your docker registry need credentials, you will need to send these HEADERS:
|
||||
|
||||
```yml
|
||||
http:
|
||||
headers:
|
||||
Access-Control-Allow-Origin: '<your docker-registry-ui url>'
|
||||
Access-Control-Allow-Credentials: true
|
||||
Access-Control-Allow-Origin: ['<your docker-registry-ui url>']
|
||||
Access-Control-Allow-Credentials: [true]
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS'] # Optional
|
||||
```
|
||||
|
||||
@@ -198,3 +232,14 @@ auth:
|
||||
realm: basic-realm
|
||||
path: /etc/docker/registry/htpasswd
|
||||
```
|
||||
|
||||
## All examples
|
||||
|
||||
- [Use docker-registry-ui as a proxy (use REGISTRY_URL)](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-proxy)
|
||||
- [Use docker-registry-ui as standalone (use URL)](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-standalone)
|
||||
- [Use docker-registry-ui with traefik](https://github.com/Joxit/docker-registry-ui/tree/master/examples/traefik)
|
||||
- [Use docker-registry-ui with docker registry and Amazon s3](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-75) ([#75](https://github.com/Joxit/docker-registry-ui/issues/88))
|
||||
- [FIX revproxy to registry does not work when published under non-root url](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-73) ([#73](https://github.com/Joxit/docker-registry-ui/issues/73))
|
||||
- [Use docker-registry-ui with HTTPS](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-20) ([#20](https://github.com/Joxit/docker-registry-ui/issues/20))
|
||||
- [Unable to push image when docker-registry-ui is used as a proxy on non 80 port](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-88) ([#88](https://github.com/Joxit/docker-registry-ui/issues/88))
|
||||
- [Add custom headers bases on environment variable and/or file when the ui is used as proxy](https://github.com/Joxit/docker-registry-ui/tree/master/examples/proxy-headers) ([#89](https://github.com/Joxit/docker-registry-ui/pull/89))
|
||||
|
||||
14
_config.yml
14
_config.yml
@@ -1,3 +1,13 @@
|
||||
title: Docker Registry v2 User Interface
|
||||
title: Docker Registry User Interface
|
||||
description: The simplest and most complete UI for your private registry!
|
||||
url: https://joxit.dev/docker-registry-ui
|
||||
google_analytics: UA-99119327-1
|
||||
theme: jekyll-theme-cayman
|
||||
theme: jekyll-theme-cayman
|
||||
author: Jones Magloire
|
||||
twitter:
|
||||
username: Joxit
|
||||
defaults:
|
||||
- scope:
|
||||
path: ""
|
||||
values:
|
||||
image: /screenshot.png
|
||||
@@ -18,6 +18,8 @@ LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
ENV NGINX_PROXY_HEADER_Host '$http_host'
|
||||
|
||||
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
|
||||
|
||||
@@ -18,6 +18,8 @@ LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
ENV NGINX_PROXY_HEADER_Host '$http_host'
|
||||
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
$@
|
||||
|
||||
sed -i "s,\${URL},${URL}," scripts/docker-registry-ui.js
|
||||
sed -i "s,\${REGISTRY_TITLE},${REGISTRY_TITLE}," scripts/docker-registry-ui.js
|
||||
sed -i "s,\${PULL_URL},${PULL_URL}," scripts/docker-registry-ui.js
|
||||
@@ -8,13 +8,31 @@ if [ -z "${DELETE_IMAGES}" ] || [ "${DELETE_IMAGES}" = false ] ; then
|
||||
sed -i -r "s/(isImageRemoveActivated[:=])[^,;]*/\1false/" scripts/docker-registry-ui.js
|
||||
fi
|
||||
|
||||
get_nginx_proxy_headers() {
|
||||
(
|
||||
env &&
|
||||
if [ -f "/etc/nginx/.env" ]; then
|
||||
cat /etc/nginx/.env
|
||||
# Force new line
|
||||
echo ""
|
||||
fi
|
||||
) | while read e; do
|
||||
if [ -n "$(echo $e | grep -o '^NGINX_PROXY_HEADER_')" ]; then
|
||||
key=$(echo ${e%%=*} | sed 's/^NGINX_PROXY_HEADER_//' | sed 's/_/-/g')
|
||||
value=${e#*=}
|
||||
echo -n "proxy_set_header ${key} \"${value}\"; "
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if [ -n "${REGISTRY_URL}" ] ; then
|
||||
sed -i "s,\${REGISTRY_URL},${REGISTRY_URL}," /etc/nginx/conf.d/default.conf
|
||||
sed -i "s^\${NGINX_PROXY_HEADERS}^$(get_nginx_proxy_headers)^" /etc/nginx/conf.d/default.conf
|
||||
sed -i "s,#!,," /etc/nginx/conf.d/default.conf
|
||||
fi
|
||||
|
||||
if [ -z "$@" ]; then
|
||||
nginx -g "daemon off;"
|
||||
exec nginx -g "daemon off;"
|
||||
else
|
||||
$@
|
||||
exec $@
|
||||
fi
|
||||
|
||||
13
bin/fill-registry
Executable file
13
bin/fill-registry
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
for i in alpine chronograf:alpine consul debian jawg/mapnik3 nginx:alpine postgres:alpine redis:alpine telegraf:alpine joxit/docker-registry-ui joxit/kosmtik joxit/node joxit/rust-openssl joxit/tile-server-ui; do
|
||||
docker pull $i
|
||||
docker tag $i 127.0.0.1:5000/$i
|
||||
docker push 127.0.0.1:5000/$i
|
||||
done
|
||||
|
||||
for i in arm32v7-static 1.2-debian-static master-static 1.2 arm64v8 arm32v7 arm64v8-static master 1.2-debian latest static debian-static debian 1.2-static 1.1 1.1-static 1.1-debian-static 1.1-debian ; do
|
||||
docker pull joxit/docker-registry-ui:$i
|
||||
docker tag joxit/docker-registry-ui:$i 127.0.0.1:5000/joxit/docker-registry-ui:$i
|
||||
docker push 127.0.0.1:5000/joxit/docker-registry-ui:$i
|
||||
done
|
||||
@@ -18,6 +18,8 @@ LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
ENV NGINX_PROXY_HEADER_Host '$http_host'
|
||||
|
||||
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
|
||||
|
||||
@@ -21,8 +21,18 @@
|
||||
<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>
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono|Roboto:300,400,700&display=swap" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta name="description" content="This is the live demo for Docker Registry User Interface. Try it now! Sources : https://github.com/Joxit/docker-registry-ui" />
|
||||
<meta property="og:description" content="This is the live demo for Docker Registry User Interface. Try it now! Sources : https://github.com/Joxit/docker-registry-ui" />
|
||||
<link rel="canonical" href="https://joxit.dev/docker-registry-ui/demo/" />
|
||||
<meta property="og:url" content="https://joxit.dev/docker-registry-ui/demo/" />
|
||||
<meta property="og:site_name" content="Live Demo | Docker Registry User Interface" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:site" content="@Joxit" />
|
||||
<meta name="twitter:creator" content="@Jones Magloire" />
|
||||
<title>Live Demo | Docker Registry User Interface</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
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><app></app><script src="scripts/vendor.js"></script><script src="scripts/docker-registry-ui.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+Mono|Roboto:300,400,700&display=swap" rel="stylesheet"><meta name="viewport" content="width=device-width,initial-scale=1"><meta property="og:site_name" content="Docker Registry UI"><meta name="twitter:card" content="summary"><meta name="twitter:site" content="@Joxit"><meta name="twitter:creator" content="@Jones Magloire"><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>
|
||||
2
dist/scripts/docker-registry-ui-static.js
vendored
2
dist/scripts/docker-registry-ui-static.js
vendored
File diff suppressed because one or more lines are too long
2
dist/scripts/docker-registry-ui.js
vendored
2
dist/scripts/docker-registry-ui.js
vendored
File diff suppressed because one or more lines are too long
2
dist/style.css
vendored
2
dist/style.css
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 764 KiB After Width: | Height: | Size: 686 KiB |
10
examples/README.md
Normal file
10
examples/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## Examples
|
||||
|
||||
- [Use docker-registry-ui as a proxy (use REGISTRY_URL)](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-proxy)
|
||||
- [Use docker-registry-ui as standalone (use URL)](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-standalone)
|
||||
- [Use docker-registry-ui with traefik](https://github.com/Joxit/docker-registry-ui/tree/master/examples/traefik)
|
||||
- [Use docker-registry-ui with docker registry and Amazon s3](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-75) ([#75](https://github.com/Joxit/docker-registry-ui/issues/88))
|
||||
- [FIX revproxy to registry does not work when published under non-root url](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-73) ([#73](https://github.com/Joxit/docker-registry-ui/issues/73))
|
||||
- [Use docker-registry-ui with HTTPS](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-20) ([#20](https://github.com/Joxit/docker-registry-ui/issues/20))
|
||||
- [Unable to push image when docker-registry-ui is used as a proxy on non 80 port](https://github.com/Joxit/docker-registry-ui/tree/master/examples/issue-88) ([#88](https://github.com/Joxit/docker-registry-ui/issues/88))
|
||||
- [Add custom headers bases on environment variable and/or file when the ui is used as proxy](https://github.com/Joxit/docker-registry-ui/tree/master/examples/proxy-headers) ([#89](https://github.com/Joxit/docker-registry-ui/pull/89))
|
||||
22
examples/helm/docker-registry-ui/.helmignore
Normal file
22
examples/helm/docker-registry-ui/.helmignore
Normal file
@@ -0,0 +1,22 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
11
examples/helm/docker-registry-ui/Chart.yaml
Normal file
11
examples/helm/docker-registry-ui/Chart.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
appVersion: "1.2.1"
|
||||
description: The simplest and most complete UI for your private registry
|
||||
name: docker-registry-ui
|
||||
home: https://github.com/Joxit/docker-registry-ui
|
||||
keywords:
|
||||
- docker
|
||||
- registry
|
||||
sources:
|
||||
- https://github.com/Joxit/docker-registry-ui
|
||||
version: 0.1.0
|
||||
97
examples/helm/docker-registry-ui/README.md
Normal file
97
examples/helm/docker-registry-ui/README.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# docker-registry-ui
|
||||
|
||||
[docker-registry-ui](https://joxit.dev/docker-registry-ui/) is the simplest and most complete UI for your private registry!
|
||||
|
||||
|
||||
## TL;DR;
|
||||
|
||||
```bash
|
||||
$ helm install .
|
||||
```
|
||||
|
||||
## Introduction
|
||||
|
||||
This chart bootstraps a [docker-registry-ui](https://joxit.dev/docker-registry-ui/) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
|
||||
|
||||
It also may deploy the [docker registry](https://docs.docker.com/registry/) if you havent have one already.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.9+ with Beta APIs enabled
|
||||
- PV provisioner support in the underlying infrastructure
|
||||
|
||||
## Installing the Chart
|
||||
|
||||
To install the chart with the release name `my-release`:
|
||||
|
||||
```bash
|
||||
$ helm update --install my-release .
|
||||
```
|
||||
|
||||
The command deploys docker-registry-ui on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation.
|
||||
|
||||
> **Tip**: List all releases using `helm list`
|
||||
|
||||
## Uninstalling the Chart
|
||||
|
||||
To uninstall/delete the `my-release` deployment:
|
||||
|
||||
```bash
|
||||
$ helm delete my-release
|
||||
```
|
||||
|
||||
The command removes all the Kubernetes components associated with the chart and deletes the release.
|
||||
|
||||
## Configuration
|
||||
|
||||
The following table lists the configurable parameters of the Redmine chart and their default values.
|
||||
|
||||
| Parameter | Description | Default |
|
||||
| --------------------------------- | ---------------------------------------- | ------------------------------------------------------- |
|
||||
| `ui.title` | Title of the managed repository | `Docker registry UI` |
|
||||
| `ui.delete_images` | Allow to delete image from the front-end | `false` |
|
||||
| `ui.proxy` | The UI service act as a proxy of the registry | `true` |
|
||||
| `ui.replicaCount` | Number of replicas to start | `1` |
|
||||
| `ui.image.registry` | registry to pull the docker-registry-ui image from | `docker.io` |
|
||||
| `ui.image.repository` | docker-registry-ui image name | `joxit/docker-registry-ui` |
|
||||
| `ui.image.tag` | docker-registry-ui image tag (change to latest to have multi registry support) | `static` |
|
||||
| `ui.image.pullPolicy` | docker-registry-ui image pull policy | `Always` |
|
||||
| `ui.probe.liveness` | Ask kubernetes to check the service port for liveness | `true` |
|
||||
| `ui.probe.readyness ` | Ask kubernetes to check the service port for readyness | `true` |
|
||||
| `ui.service.type` | Desired service type | `ClusterIP` |
|
||||
| `ui.service.port` | Service exposed port | `80` |
|
||||
| `ui.ingress.enabled` | Create an ingress for docker-regstry-ui | `false` |
|
||||
| `registry.external` | Use an already available registry | `false` |
|
||||
| `registry.url` | URL of the existing registry | `http://localhost:5000` |
|
||||
| `registry.replicaCount` | Number of replicas to start | `1` |
|
||||
| `registry.image.registry` | registry to pull the docker-registry image from | `docker.io` |
|
||||
| `registry.image.repository` | docker-registry-ui image name | `registry` |
|
||||
| `registry.image.tag` | docker-registry-ui image tag | `2.6.2` |
|
||||
| `registry.image.pullPolicy` | docker-registry-ui image pull policy | `Always` |
|
||||
| `registry.probe.liveness` | Ask kubernetes to check the service port for liveness | `true` |
|
||||
| `registry.probe.readyness ` | Ask kubernetes to check the service port for readyness | `true` |
|
||||
| `registry.persistence.enabled` | Enable persistence using PVC for the registry | `false` |
|
||||
| `registry.persistence.storageClass` | PVC Storage Class | `-` |
|
||||
| `registry.persistence.size` | PVC Storage Request size | `1Gi` |
|
||||
| `registry.service.type` | Desired service type | `ClusterIP` |
|
||||
| `registry.service.port` | Service exposed port | `5000` |
|
||||
| `registry.ingress.enabled` | Create an ingress for the regstry | `false` |
|
||||
|
||||
|
||||
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
|
||||
|
||||
```bash
|
||||
$ helm upgrade --install my-release \
|
||||
--set registry.external=true \
|
||||
--set registry.url=http://registry.example.com:5000 \
|
||||
.
|
||||
```
|
||||
|
||||
Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
|
||||
|
||||
```bash
|
||||
$ helm upgrade --install my-release -f values.yaml .
|
||||
```
|
||||
|
||||
> **Tip**: You can use the default [values.yaml](values.yaml)
|
||||
|
||||
147
examples/helm/docker-registry-ui/templates/_helpers.tpl
Normal file
147
examples/helm/docker-registry-ui/templates/_helpers.tpl
Normal file
@@ -0,0 +1,147 @@
|
||||
{{/* vim: set filetype=mustache: */}}
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "docker-registry-ui.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "docker-registry-ui.fullname" -}}
|
||||
{{- if .Values.ui.fullnameOverride -}}
|
||||
{{- .Values.ui.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- if contains $name .Release.Name -}}
|
||||
{{- printf "%s-ui" .Release.Name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-ui-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "docker-registry.fullname" -}}
|
||||
{{- if .Values.registry.fullnameOverride -}}
|
||||
{{- .Values.registry.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- if contains $name .Release.Name -}}
|
||||
{{- printf "%s-registry" .Release.Name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-registry-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "docker-registry-ui.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "docker-registry-ui.labels" -}}
|
||||
app: registry-ui
|
||||
chart: {{ include "docker-registry-ui.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "docker-registry-ui.matchLabels" -}}
|
||||
app: registry-ui
|
||||
release: {{ .Release.Name }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "docker-registry.labels" -}}
|
||||
app: registry
|
||||
chart: {{ include "docker-registry-ui.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "docker-registry.matchLabels" -}}
|
||||
app: registry
|
||||
release: {{ .Release.Name }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "docker-registry-ui.probes" -}}
|
||||
{{- if and .Values.ui.probe.liveness (eq .Values.ui.probe.liveness true) -}}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
{{- end -}}
|
||||
{{- if and .Values.ui.probe.readiness (eq .Values.ui.probe.readiness true) }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "docker-registry.probes" -}}
|
||||
{{- if and .Values.registry.probe.liveness (eq .Values.registry.probe.liveness true) -}}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /v2/
|
||||
port: registry
|
||||
{{- end -}}
|
||||
{{- if and .Values.registry.probe.readiness (eq .Values.registry.probe.readiness true) }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /v2/
|
||||
port: registry
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "docker-registry-ui.url-name" -}}
|
||||
{{- if eq .Values.ui.proxy true -}}
|
||||
REGISTRY_URL
|
||||
{{- else -}}
|
||||
URL
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "docker-registry-ui.url-value" -}}
|
||||
{{- if eq .Values.registry.external true -}}
|
||||
{{ .Values.registry.url }}
|
||||
{{- else -}}
|
||||
{{- $fullName := include "docker-registry.fullname" . -}}
|
||||
{{ printf "http://%s.%s:%.0f" $fullName .Release.Namespace .Values.registry.service.port }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "docker-registry-ui.pull" -}}
|
||||
{{- if eq .Values.registry.external true -}}
|
||||
{{ .Values.registry.url }}
|
||||
{{- else -}}
|
||||
{{- if eq .Values.ui.proxy true -}}
|
||||
{{- if eq .Values.ui.ingress.enabled true -}}
|
||||
{{- $host := index .Values.ui.ingress.hosts 0 -}}
|
||||
{{ $host.host }}
|
||||
{{- else -}}
|
||||
{{- $fullName := include "docker-registry-ui.fullname" . -}}
|
||||
{{ printf "%s.%s:%.0f" $fullName .Release.Namespace .Values.ui.service.port }}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- if eq .Values.registry.ingress.enabled true -}}
|
||||
{{- $host := index .Values.registry.ingress.hosts 0 -}}
|
||||
{{ $host.host }}
|
||||
{{- else -}}
|
||||
{{- $fullName := include "docker-registry.fullname" . -}}
|
||||
{{ printf "%s.%s:%.0f" $fullName .Release.Namespace .Values.registry.service.port }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,31 @@
|
||||
{{- if eq .Values.registry.external false -}}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "docker-registry.fullname" . }}
|
||||
labels:
|
||||
{{ include "docker-registry.labels" . | indent 4 }}
|
||||
data:
|
||||
config.yml: |-
|
||||
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: ['*']
|
||||
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']
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,62 @@
|
||||
{{- if eq .Values.registry.external false -}}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "docker-registry.fullname" . }}
|
||||
labels:
|
||||
{{ include "docker-registry.labels" . | indent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.registry.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{ include "docker-registry.matchLabels" . | indent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{ include "docker-registry.matchLabels" . | indent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
defaultMode: 420
|
||||
name: {{ include "docker-registry.fullname" . }}
|
||||
- name: data
|
||||
{{- if .Values.registry.persistence.enabled }}
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "docker-registry.fullname" . }}
|
||||
{{- else }}
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: registry
|
||||
image: "{{ .Values.registry.image.registry }}/{{ .Values.registry.image.repository }}:{{ .Values.registry.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.registry.image.pullPolicy }}
|
||||
ports:
|
||||
- name: registry
|
||||
containerPort: 5000
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- mountPath: "/var/lib/registry"
|
||||
name: "data"
|
||||
- mountPath: "/etc/docker/registry"
|
||||
name: "config"
|
||||
{{ include "docker-registry.probes" . | indent 10 }}
|
||||
resources:
|
||||
{{- toYaml .Values.registry.resources | nindent 12 }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
34
examples/helm/docker-registry-ui/templates/reg-ingress.yaml
Normal file
34
examples/helm/docker-registry-ui/templates/reg-ingress.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
{{- if and (eq .Values.registry.external false) (and (eq .Values.ui.proxy false) .Values.registry.ingress.enabled) -}}
|
||||
{{- $fullName := include "docker-registry.fullname" . -}}
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{ include "docker-registry.labels" . | indent 4 }}
|
||||
{{- with .Values.registry.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.registry.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.registry.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.registry.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: registry
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
23
examples/helm/docker-registry-ui/templates/reg-pvc.yaml
Normal file
23
examples/helm/docker-registry-ui/templates/reg-pvc.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
{{- if and (eq .Values.registry.external false) .Values.registry.persistence.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
labels:
|
||||
{{ include "docker-registry.labels" . | indent 4 }}
|
||||
name: {{ include "docker-registry.fullname" . }}
|
||||
spec:
|
||||
accessModes:
|
||||
{{- range .Values.registry.persistence.accessModes }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.registry.persistence.size }}
|
||||
{{- if .Values.registry.persistence.storageClass }}
|
||||
{{- if (eq "-" .Values.registry.persistence.storageClass) }}
|
||||
storageClassName: ""
|
||||
{{- else }}
|
||||
storageClassName: {{ .Values.registry.persistence.storageClass | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
17
examples/helm/docker-registry-ui/templates/reg-service.yaml
Normal file
17
examples/helm/docker-registry-ui/templates/reg-service.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
{{- if eq .Values.registry.external false -}}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "docker-registry.fullname" . }}
|
||||
labels:
|
||||
{{ include "docker-registry.labels" . | indent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.registry.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.registry.service.port }}
|
||||
targetPort: registry
|
||||
protocol: TCP
|
||||
name: registry
|
||||
selector:
|
||||
{{ include "docker-registry.matchLabels" . | indent 6 }}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,52 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "docker-registry-ui.fullname" . }}
|
||||
labels:
|
||||
{{ include "docker-registry-ui.labels" . | indent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.ui.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{ include "docker-registry-ui.matchLabels" . | indent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{ include "docker-registry-ui.matchLabels" . | indent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: registry-ui
|
||||
image: "{{ .Values.ui.image.registry }}/{{ .Values.ui.image.repository }}:{{ .Values.ui.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.ui.image.pullPolicy }}
|
||||
env:
|
||||
- name: REGISTRY_TITLE
|
||||
value: {{ .Values.ui.title| quote }}
|
||||
- name: DELETE_IMAGES
|
||||
value: {{ .Values.ui.delete_images| quote }}
|
||||
- name: {{ include "docker-registry-ui.url-name" . }}
|
||||
value: {{ include "docker-registry-ui.url-value" . | quote }}
|
||||
- name: PULL_URL
|
||||
value: {{ include "docker-registry-ui.pull" . | quote }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
{{ include "docker-registry-ui.probes" . | indent 10 }}
|
||||
resources:
|
||||
{{- toYaml .Values.ui.resources | nindent 12 }}
|
||||
{{- with .Values.ui.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
34
examples/helm/docker-registry-ui/templates/ui-ingress.yaml
Normal file
34
examples/helm/docker-registry-ui/templates/ui-ingress.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
{{- if .Values.ui.ingress.enabled -}}
|
||||
{{- $fullName := include "docker-registry-ui.fullname" . -}}
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{ include "docker-registry-ui.labels" . | indent 4 }}
|
||||
{{- with .Values.ui.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ui.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ui.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ui.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: http
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
15
examples/helm/docker-registry-ui/templates/ui-service.yaml
Normal file
15
examples/helm/docker-registry-ui/templates/ui-service.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "docker-registry-ui.fullname" . }}
|
||||
labels:
|
||||
{{ include "docker-registry-ui.labels" . | indent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.ui.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.ui.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{ include "docker-registry-ui.matchLabels" . | indent 6 }}
|
||||
129
examples/helm/docker-registry-ui/values.yaml
Normal file
129
examples/helm/docker-registry-ui/values.yaml
Normal file
@@ -0,0 +1,129 @@
|
||||
# Default values for docker-registry-ui.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
|
||||
ui:
|
||||
# title of the registry
|
||||
title: "Docker registry UI"
|
||||
# allow delete of images
|
||||
delete_images: false
|
||||
# UI behave as a proxy of the registry
|
||||
proxy: true
|
||||
|
||||
replicaCount: 1
|
||||
image:
|
||||
registry: docker.io
|
||||
repository: joxit/docker-registry-ui
|
||||
tag: static
|
||||
pullPolicy: Always
|
||||
probe:
|
||||
liveness: true
|
||||
readiness: true
|
||||
|
||||
resources: {}
|
||||
# If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
fullnameOverride: ""
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
hosts:
|
||||
- host: docker-registry-ui.local
|
||||
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - chart-example.local
|
||||
|
||||
|
||||
registry:
|
||||
external: false
|
||||
# URL of the registry (requiered. Note: this wont work as localhost is inside the container. Only used if the registry is external)
|
||||
url: http://localhost:5000
|
||||
|
||||
replicaCount: 1
|
||||
# Image definition for the registry (Only used if the registry is not external)
|
||||
image:
|
||||
registry: docker.io
|
||||
repository: registry
|
||||
tag: 2.6.2
|
||||
pullPolicy: Always
|
||||
probe:
|
||||
liveness: true
|
||||
readiness: true
|
||||
resources: {}
|
||||
# If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
fullnameOverride: ""
|
||||
|
||||
|
||||
persistence:
|
||||
## If true, use a Persistent Volume Claim, If false, use emptyDir
|
||||
##
|
||||
enabled: false
|
||||
## Persistent Volume Storage Class
|
||||
## If defined, storageClassName: <storageClass>
|
||||
## If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||
## If undefined (the default) or set to null, no storageClassName spec is
|
||||
## set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||
## GKE, AWS & OpenStack)
|
||||
##
|
||||
# storageClass: "-"
|
||||
## Persistent Volume Claim annotations
|
||||
##
|
||||
annotations:
|
||||
## Persistent Volume Access Mode
|
||||
##
|
||||
accessModes:
|
||||
# This have to be ReadWriteMany if replicaCount>1
|
||||
- ReadWriteOnce
|
||||
## Persistent Volume size
|
||||
##
|
||||
size: 1Gi
|
||||
##
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 5000
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
hosts:
|
||||
- host: docker-registry.local
|
||||
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - chart-example.local
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
11
examples/issue-20/README.md
Normal file
11
examples/issue-20/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Example for issue #20 (HTTPS supports)
|
||||
|
||||
This example will override the original nginx conf with one supporting HTTPS. You will need to rewrite all the project configuration (replaces `proxy_pass` with our value).
|
||||
|
||||
Generating a self signed certificate:
|
||||
|
||||
```
|
||||
openssl req -newkey rsa:2048 -nodes -keyout nginx/privkey.pem -x509 -days 3650 -out nginx/fullchain.pem
|
||||
```
|
||||
|
||||
The UI will be available here : https://localhost
|
||||
27
examples/issue-20/docker-compose.yml
Normal file
27
examples/issue-20/docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
networks:
|
||||
- registry-ui-net
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
- ./nginx/fullchain.pem:/etc/nginx/certs/fullchain.pem
|
||||
- ./nginx/privkey.pem:/etc/nginx/certs/privkey.pem
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
depends_on:
|
||||
- registry
|
||||
networks:
|
||||
- registry-ui-net
|
||||
|
||||
networks:
|
||||
registry-ui-net:
|
||||
21
examples/issue-20/nginx/fullchain.pem
Normal file
21
examples/issue-20/nginx/fullchain.pem
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDYDCCAkigAwIBAgIJAKNtVPbuycx+MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMTkwNDE2MDk1NzEzWhcNMjkwNDEzMDk1NzEzWjBF
|
||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||
CgKCAQEAuykmuGBPiNDWzxmqK7BQgJqDLWbAsf4769sI2gSMR0C6qd6WV6JJ+Rf+
|
||||
y1auT2fA38cvJUjdPBEQCTxpE3Ce3e9nXYTITzze6OUCwewbdR/Cm+dHyR+M2YNP
|
||||
SQrZI6p4NE1TwCHc0LVWfblAaWiylFPeWlFCVSg5hqKAkRh9PEcWBdN5vim3/8sC
|
||||
16YmXWCERGPdFKYBN52ERJ+9h51ktMdns0LJVn+DLVSNWsiH76IMulHU64d9nZoL
|
||||
kVhxohiOeP2ZuV7E+9RYDlaKObohclPz3RoOXUbr3zjjna+dqxI6mxCw5qms26RL
|
||||
eBcQQA/EoqaAv+y+jCKqbCCcEgy27QIDAQABo1MwUTAdBgNVHQ4EFgQUDKyOzsPn
|
||||
Tc6ZTTdnt8U59/j+3l8wHwYDVR0jBBgwFoAUDKyOzsPnTc6ZTTdnt8U59/j+3l8w
|
||||
DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAmqHfSjO58FoJWJUM
|
||||
2i0rcql0Y24XjZ92RdBQGLkvAhi+QxWBXNKibvpen2miv3fAeYmiFtIHQCuOCqCj
|
||||
SSdQwb91D5WR9s21PILEWsOd1H0v4ZVHX2Z5Qv5f6Hk1DiTG/sZmzUqog74TtCpG
|
||||
4m56/JYd4Mkk9raiWT9RKVTVnSHjM8h2zIMio14Nil4zO67G68jp1K0C1AM9npsf
|
||||
cvQ2+2XAOEcQ7e3nCF4ppA3HdnCm8qbr8DM12KTs+nkncps/7u+3C5vv5TxI+BEz
|
||||
b5Cs+HbLwPAphYp0CSK+sXiCUMA//mUAcMeYKq2/V4wufJlZEpBxogdttW7J4KJm
|
||||
Num0pw==
|
||||
-----END CERTIFICATE-----
|
||||
24
examples/issue-20/nginx/nginx.conf
Normal file
24
examples/issue-20/nginx/nginx.conf
Normal file
@@ -0,0 +1,24 @@
|
||||
server {
|
||||
listen 443 ssl;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
location /v2 {
|
||||
# Do not allow connections from docker 1.5 and earlier
|
||||
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
|
||||
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(.[0-9]-dev))|Go ).*$" ) {
|
||||
return 404;
|
||||
}
|
||||
proxy_pass http://registry:5000;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
28
examples/issue-20/nginx/privkey.pem
Normal file
28
examples/issue-20/nginx/privkey.pem
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7KSa4YE+I0NbP
|
||||
GaorsFCAmoMtZsCx/jvr2wjaBIxHQLqp3pZXokn5F/7LVq5PZ8Dfxy8lSN08ERAJ
|
||||
PGkTcJ7d72ddhMhPPN7o5QLB7Bt1H8Kb50fJH4zZg09JCtkjqng0TVPAIdzQtVZ9
|
||||
uUBpaLKUU95aUUJVKDmGooCRGH08RxYF03m+Kbf/ywLXpiZdYIREY90UpgE3nYRE
|
||||
n72HnWS0x2ezQslWf4MtVI1ayIfvogy6UdTrh32dmguRWHGiGI54/Zm5XsT71FgO
|
||||
Voo5uiFyU/PdGg5dRuvfOOOdr52rEjqbELDmqazbpEt4FxBAD8SipoC/7L6MIqps
|
||||
IJwSDLbtAgMBAAECggEBAK7E+KFHXj22NkDiCGQPmrzcjA4DW4FalH3j5Vog0RVg
|
||||
Pm6NqfpfU5BFdepPISqJCjRs/XtllSGYFU9ql/xNOCyqd+1+JsbHYqg74d1QKzut
|
||||
0r5etEv9KDudQJZGiQmjD+hXJRPPCzHhg8iXCqzj1Y5o2sOgCb8XdtBgQoo7Qgbc
|
||||
CG+3tytGPo33dotiFBUknrQRexTwgSWYXI89lI6fRSJlc8NyK7zp+mGbSopqGWHm
|
||||
X6V8AI+XNuliIhTvOxGhw0maNEnds39SYHCYfLATjp9x6XVVp5mG7BJLkifC8Cob
|
||||
IYQGfBwmEYbOTiNJ6oEgRZOZFPsLbqsPfPgTpHvIwUECgYEA7WfYek3DWkC7Ex3r
|
||||
7hcZjBa8JMxPhgSMho/5F8zHGAf8MdEmXPYKi9tvhLeMJQwzzlN4RtX9zg0FJ5eL
|
||||
tSgGHT/aRc2/9ZAvuG7gypNZlaAd+/SloYfKsGJQxFqLTfm288qyrRoOtBjhRMCI
|
||||
lRmw5uYVV775cK741+lyD5xj/DkCgYEAydHb8mIt/IvCloVGzP8z4veIarEecYk3
|
||||
UPw/wneZFZwGegGTsCwxox1uWVcO5CoNLhRo7622kZ6Mhsd83ySj8eQWpR1qoeMJ
|
||||
8ti9c2FniZdtUwdFgu7GPgJq3DWTVQ0c0MTnyk/UbsfD/AKG0YK9T2sReteaPOUg
|
||||
nohVutYZuFUCgYEAorXau7BSZKgaz3ZhfjQc0VO/rWTOWCcD/THt4i76gXDvm2Ei
|
||||
bvI+ti42V3rJNZcQZqf0tm/x3Og1kTYfjZCZ6DAcNF1Y5D/nRPvRW2X0L0WnZ0j8
|
||||
wCHmfE9V1c3MziuJBbv2DAfg7fRjaJCgy7fo88fb9uCv61gwuyKHh0WDjZkCgYB3
|
||||
R89lLF3dm4TAjbjQxCyYgpBf7pr9o4nMFaphd2pE+Vhil7gAMb6Ml4J2zxuAAtKT
|
||||
X4C917/FxR1tM048XF2BQ7uWjxJM5/EjVLJ0FSeqjJMStYOB5TnJwIgD6q5PYFad
|
||||
lSMh0ZjOeMb+lUe1YD4fSDqmjfMc9pcW26E/sfa1mQKBgQDMRKH/R+yw6Nemu837
|
||||
mwNVTiKtQoWS8jl8Gwox6o3cgrV/6szQaQz8oF9x829jFehYEMGYMX/8zPToyBCU
|
||||
gRod6bcMmdLB8EQd7VI5L9/CeoZQmpVVZ1STNjUqscE/Gb98nCPNXTkVeAgtE1WS
|
||||
AVhAAc+34wOxlAcjcXweBK69kg==
|
||||
-----END PRIVATE KEY-----
|
||||
31
examples/issue-73/docker-compose.yml
Normal file
31
examples/issue-73/docker-compose.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
networks:
|
||||
- registry-ui-net
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
- REGISTRY_URL=http://registry:5000
|
||||
depends_on:
|
||||
- registry
|
||||
networks:
|
||||
- registry-ui-net
|
||||
|
||||
proxy:
|
||||
image: nginx:alpine
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
ports:
|
||||
- 80:80
|
||||
depends_on:
|
||||
- ui
|
||||
networks:
|
||||
- registry-ui-net
|
||||
networks:
|
||||
registry-ui-net:
|
||||
35
examples/issue-73/nginx.conf
Normal file
35
examples/issue-73/nginx.conf
Normal file
@@ -0,0 +1,35 @@
|
||||
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;
|
||||
# required for strict SNI checking: see Issue #70 (https://github.com/Joxit/docker-registry-ui/issues/70)
|
||||
proxy_ssl_server_name on;
|
||||
|
||||
location /ui/ {
|
||||
proxy_pass http://ui/;
|
||||
}
|
||||
|
||||
#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;
|
||||
#}
|
||||
}
|
||||
7
examples/issue-75/README.md
Normal file
7
examples/issue-75/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Example for issue #75
|
||||
|
||||
Run this command `docker-compose up -d`, then you can push your images (e.g localhost:5000/alpine).
|
||||
|
||||
Be careful, the docker registry is using status codes 307 for each requests, that means you must configure your s3 to accept same requests as your private registry (that means `DELETE`, `Access-Control-Allow-Origin` and others). To avoid this, we need the option `storage.redirect.disable: true`, with this you will use your registry credentials (if you are using it).
|
||||
|
||||
This s3 server allow all requests.
|
||||
32
examples/issue-75/docker-compose.yml
Normal file
32
examples/issue-75/docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.7
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
- ./registry-config/config.yml:/etc/docker/registry/config.yml
|
||||
depends_on:
|
||||
- s3-server
|
||||
network_mode: host
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- URL=http://127.0.0.1:5000
|
||||
- DELETE_IMAGES=true
|
||||
depends_on:
|
||||
- registry
|
||||
network_mode: host
|
||||
|
||||
s3-server:
|
||||
image: minio/minio:RELEASE.2019-04-09T01-22-30Z
|
||||
volumes:
|
||||
- ./s3-server-cmd:/bin/s3-server-cmd:ro
|
||||
environment:
|
||||
- MINIO_ACCESS_KEY=accessKey1
|
||||
- MINIO_SECRET_KEY=verySecretKey1
|
||||
- MINIO_REGION=us-east-1
|
||||
network_mode: host
|
||||
entrypoint: /bin/s3-server-cmd
|
||||
39
examples/issue-75/registry-config/config.yml
Normal file
39
examples/issue-75/registry-config/config.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
version: 0.1
|
||||
|
||||
log:
|
||||
level: debug
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
delete:
|
||||
enabled: true
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
s3:
|
||||
accesskey: accessKey1
|
||||
secretkey: verySecretKey1
|
||||
region: us-east-1
|
||||
regionendpoint: http://127.0.0.1:9000
|
||||
bucket: registry
|
||||
encrypt: false
|
||||
secure: false
|
||||
v4auth: true
|
||||
chunksize: 5242880
|
||||
rootdirectory: /
|
||||
redirect:
|
||||
disable: true
|
||||
http:
|
||||
addr: 0.0.0.0:5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
Access-Control-Allow-Origin: ['http://127.0.0.1']
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
|
||||
Access-Control-Allow-Headers: ['Authorization']
|
||||
Access-Control-Max-Age: [1728000]
|
||||
Access-Control-Allow-Credentials: [true]
|
||||
Access-Control-Expose-Headers: ['Docker-Content-Digest']
|
||||
health:
|
||||
storagedriver:
|
||||
enabled: true
|
||||
interval: 10s
|
||||
threshold: 3
|
||||
6
examples/issue-75/s3-server-cmd
Executable file
6
examples/issue-75/s3-server-cmd
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
mkdir -p /data/registry
|
||||
minio server /data
|
||||
4
examples/issue-88/README.md
Normal file
4
examples/issue-88/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Example for issue #88
|
||||
|
||||
When the docker registry ui is used as a proxy and its port is not 80, we can't push images.
|
||||
To fix that, I added the correct Host header in the proxy_set_header.
|
||||
23
examples/issue-88/config.yml
Normal file
23
examples/issue-88/config.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
version: 0.1
|
||||
log:
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
delete:
|
||||
enabled: true
|
||||
http:
|
||||
addr: :5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
Access-Control-Allow-Origin: ['*']
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
|
||||
Access-Control-Expose-Headers: ['Docker-Content-Digest']
|
||||
health:
|
||||
storagedriver:
|
||||
enabled: true
|
||||
interval: 10s
|
||||
threshold: 3
|
||||
34
examples/issue-88/docker-compose.yml
Normal file
34
examples/issue-88/docker-compose.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
registry-srv:
|
||||
image: registry:latest
|
||||
restart: always
|
||||
volumes:
|
||||
- storage:/var/lib/registry
|
||||
- ./config.yml:/etc/docker/registry/config.yml:ro
|
||||
networks:
|
||||
- registry-ui-net
|
||||
container_name: registry-srv
|
||||
|
||||
registry-ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
- REGISTRY_TITLE=Private Docker Registry
|
||||
- REGISTRY_URL=http://registry-srv:5000
|
||||
- DELETE_IMAGES=true
|
||||
depends_on:
|
||||
- debugproxy
|
||||
networks:
|
||||
- registry-ui-net
|
||||
container_name: registry-ui
|
||||
|
||||
networks:
|
||||
registry-ui-net:
|
||||
|
||||
volumes:
|
||||
storage:
|
||||
driver: local
|
||||
19
examples/kubernetes/README.md
Normal file
19
examples/kubernetes/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Kubernetes installation of Docker Registry UI
|
||||
|
||||
## Full installation
|
||||
Install a registry and docker-registry-ui as frontend of this registry to kubernetes.
|
||||
|
||||
```sh
|
||||
kubectl apply -f *.yaml
|
||||
```
|
||||
|
||||
Please note that you'll need a PV provisionner to be able to store the uploaded images.
|
||||
|
||||
## Dynamic installation
|
||||
Edit the image tag in the ui-deployement.yaml file and set it to `latest`, then :
|
||||
|
||||
```sh
|
||||
kubectl apply -f ui*.yaml
|
||||
```
|
||||
|
||||
You'll get a docker-registry-ui pod installed inside kubernetes and you'll be able to configure it to act as a frontend to your existing registry(ies).
|
||||
31
examples/kubernetes/registry-configmap.yaml
Normal file
31
examples/kubernetes/registry-configmap.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: docker-registry
|
||||
labels:
|
||||
app: registry
|
||||
release: docker-registry-ui
|
||||
app/version: "1.2.1"
|
||||
data:
|
||||
config.yml: |-
|
||||
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: ['*']
|
||||
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']
|
||||
51
examples/kubernetes/registry-deployment.yaml
Normal file
51
examples/kubernetes/registry-deployment.yaml
Normal file
@@ -0,0 +1,51 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: docker-registry
|
||||
labels:
|
||||
app: registry
|
||||
release: docker-registry-ui
|
||||
app/version: "1.2.1"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: registry
|
||||
release: docker-registry-ui
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: registry
|
||||
release: docker-registry-ui
|
||||
spec:
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
defaultMode: 420
|
||||
name: docker-registry
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: docker-registry
|
||||
containers:
|
||||
- name: registry
|
||||
image: "docker.io/registry:2.6.2"
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: registry
|
||||
containerPort: 5000
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- mountPath: "/var/lib/registry"
|
||||
name: "data"
|
||||
- mountPath: "/etc/docker/registry"
|
||||
name: "config"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /v2/
|
||||
port: registry
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /v2/
|
||||
port: registry
|
||||
resources:
|
||||
{}
|
||||
14
examples/kubernetes/registry-pvc.yaml
Normal file
14
examples/kubernetes/registry-pvc.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
labels:
|
||||
app: registry
|
||||
release: docker-registry-ui
|
||||
app/version: "1.2.1"
|
||||
name: docker-registry
|
||||
spec:
|
||||
accessModes:
|
||||
- "ReadWriteOnce"
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
18
examples/kubernetes/registry-service.yaml
Normal file
18
examples/kubernetes/registry-service.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: docker-registry
|
||||
labels:
|
||||
app: registry
|
||||
release: docker-registry-ui
|
||||
app/version: "1.2.1"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 5000
|
||||
targetPort: registry
|
||||
protocol: TCP
|
||||
name: registry
|
||||
selector:
|
||||
app: registry
|
||||
release: docker-registry-ui
|
||||
48
examples/kubernetes/ui-deployment.yaml
Normal file
48
examples/kubernetes/ui-deployment.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: docker-registry-ui
|
||||
labels:
|
||||
app: registry-ui
|
||||
release: docker-registry-ui
|
||||
app/version: "1.2.1"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: registry-ui
|
||||
release: docker-registry-ui
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: registry-ui
|
||||
release: docker-registry-ui
|
||||
spec:
|
||||
containers:
|
||||
- name: registry-ui
|
||||
image: "docker.io/joxit/docker-registry-ui:static"
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: REGISTRY_TITLE
|
||||
value: "Docker registry UI"
|
||||
- name: DELETE_IMAGES
|
||||
value: "false"
|
||||
- name: REGISTRY_URL
|
||||
value: "http://docker-registry.default:5000"
|
||||
- name: PULL_URL
|
||||
value: "docker-registry-ui.default:80"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
resources:
|
||||
{}
|
||||
|
||||
19
examples/kubernetes/ui-service.yaml
Normal file
19
examples/kubernetes/ui-service.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: docker-registry-ui
|
||||
labels:
|
||||
app: registry-ui
|
||||
release: docker-registry-ui
|
||||
app/version: "1.2.1"
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: registry-ui
|
||||
release: docker-registry-ui
|
||||
|
||||
19
examples/proxy-headers/README.md
Normal file
19
examples/proxy-headers/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Set custom headers to the registry example
|
||||
|
||||
The interface and the docker registry will be accessible with <http://localhost>.
|
||||
|
||||
This example highlight the usage of custom headers when the UI is used as a proxy. When you wants to use a header name with hyphens, replace them by underscores in the variable. You can put headers in environment variable or in config file `/etc/nginx/.env`. They have the same writing style.
|
||||
|
||||
Headers can be useful in some cases such as avoid sending credentials when you are on the UI. Or give to the registry server other properties such as X-Forward-For header.
|
||||
|
||||
I will set these two headers in this example. X-Forward-For by environment variable and Authorization by file.
|
||||
|
||||
In order to set your credentials in the header, you need to know how [Authorization](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) header works. Here we use the `Basic` authentication scheme, the credentials are constructed like this:
|
||||
- The username and the password are combined with a colon (`registry:ui`).
|
||||
- The resulting string is base64 encoded (`cmVnaXN0cnk6dWk=`). You can simply run `echo -n "registry:ui" | base64`.
|
||||
- In your header, put this value `Basic cmVnaXN0cnk6dWk=`
|
||||
- In your `/etc/nginx/.env`, the file will contains `NGINX_PROXY_HEADER_Authorization=Basic cmVnaXN0cnk6dWk=`
|
||||
|
||||
For X-Forward-For, replace all hyphens by underscores, and the value will be a nginx variable which is `$proxy_add_x_forwarded_for`. In your docker compose you will need to duplicate the `$` character. In your docker-compose, your environment will look like `NGINX_PROXY_HEADER_X_Forwarded_For=$$proxy_add_x_forwarded_for`
|
||||
|
||||
As usual, run the project with `docker-compose up -d` (for background mode)
|
||||
28
examples/proxy-headers/docker-compose.yml
Normal file
28
examples/proxy-headers/docker-compose.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.7
|
||||
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
|
||||
- NGINX_PROXY_HEADER_X_Forwarded_For=$$proxy_add_x_forwarded_for
|
||||
volumes:
|
||||
- ./nginx.env:/etc/nginx/.env
|
||||
depends_on:
|
||||
- registry
|
||||
networks:
|
||||
- registry-ui-net
|
||||
|
||||
networks:
|
||||
registry-ui-net:
|
||||
1
examples/proxy-headers/nginx.env
Normal file
1
examples/proxy-headers/nginx.env
Normal file
@@ -0,0 +1 @@
|
||||
NGINX_PROXY_HEADER_Authorization=Basic cmVnaXN0cnk6dWk=
|
||||
25
examples/proxy-headers/registry-config/credentials.yml
Normal file
25
examples/proxy-headers/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/proxy-headers/registry-config/htpasswd
Normal file
1
examples/proxy-headers/registry-config/htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
registry:$2y$11$1bmuJLK8HrQl5ACS/WeqRuJLUArUZfUcP2R23asmozEpfN76.pCHy
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
networks:
|
||||
- docker-registry-ui
|
||||
- registry-ui-net
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
depends_on:
|
||||
- registry
|
||||
networks:
|
||||
- docker-registry-ui
|
||||
- registry-ui-net
|
||||
|
||||
networks:
|
||||
registry-ui-net:
|
||||
registry-ui-net:
|
||||
|
||||
@@ -20,14 +20,16 @@ const allTags = ['src/tags/*.tag', 'src/tags/dialogs/*.tag'];
|
||||
|
||||
const allScripts = [
|
||||
'src/scripts/http.js',
|
||||
'src/scripts/script.js'
|
||||
'src/scripts/script.js',
|
||||
'src/scripts/utils.js'
|
||||
];
|
||||
|
||||
const staticTags = ['src/tags/*.tag'];
|
||||
|
||||
const staticScripts = [
|
||||
'src/scripts/http.js',
|
||||
'src/scripts/static.js'
|
||||
'src/scripts/static.js',
|
||||
'src/scripts/utils.js'
|
||||
];
|
||||
|
||||
function html() {
|
||||
|
||||
@@ -24,6 +24,7 @@ server {
|
||||
#! if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
|
||||
#! return 404;
|
||||
#! }
|
||||
#! ${NGINX_PROXY_HEADERS}
|
||||
#! proxy_pass ${REGISTRY_URL};
|
||||
#! }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docker-registry-ui",
|
||||
"version": "1.1.0",
|
||||
"version": "1.4.2",
|
||||
"scripts": {
|
||||
"build": "./node_modules/gulp/bin/gulp.js build"
|
||||
},
|
||||
@@ -14,8 +14,8 @@
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"del": "^3.0.0",
|
||||
"gulp": "^4.0",
|
||||
"gulp-clean-css": "^4.0.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-clean-css": "^4.2.0",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-filter": "^5.1.0",
|
||||
"gulp-htmlmin": "^5.0.1",
|
||||
@@ -23,7 +23,7 @@
|
||||
"gulp-inject-version": "^1.0.1",
|
||||
"gulp-license": "^1.1.0",
|
||||
"gulp-riot": "^1.1.5",
|
||||
"gulp-uglify": "^3.0.1",
|
||||
"gulp-uglify": "^3.0.2",
|
||||
"gulp-useref": "^3.1.6",
|
||||
"riot": "^3.13.2",
|
||||
"riot-mui": "^0.1.1",
|
||||
|
||||
@@ -20,13 +20,18 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- build:css vendor.css -->
|
||||
<LINK href="../node_modules/riot-mui/build/styles/riot-mui.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">
|
||||
<LINK href="material-icons.css" rel="stylesheet" type="text/css">
|
||||
<link href="style.css" rel="stylesheet" type="text/css">
|
||||
<link href="material-icons.css" rel="stylesheet" type="text/css">
|
||||
<!-- endbuild -->
|
||||
<LINK href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&lang=en" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono|Roboto:300,400,700&display=swap" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta property="og:site_name" content="Docker Registry UI" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:site" content="@Joxit" />
|
||||
<meta name="twitter:creator" content="@Jones Magloire" />
|
||||
<title>Docker Registry UI</title>
|
||||
</head>
|
||||
|
||||
@@ -53,9 +58,12 @@
|
||||
<script src="tags/dialogs/menu.tag" type="riot/tag"></script>
|
||||
<script src="tags/image-size.tag" type="riot/tag"></script>
|
||||
<script src="tags/image-date.tag" type="riot/tag"></script>
|
||||
<script src="tags/image-content-digest.tag" type="riot/tag"></script>
|
||||
<script src="tags/pagination.tag" type="riot/tag"></script>
|
||||
<script src="tags/app.tag" type="riot/tag"></script>
|
||||
<script src="scripts/http.js"></script>
|
||||
<script src="scripts/script.js"></script>
|
||||
<script src="scripts/utils.js"></script>
|
||||
<!-- endbuild -->
|
||||
</body>
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(MaterialIcons-Regular.eot); /* For IE6-8 */
|
||||
src: local('fonts/Material Icons'),
|
||||
local('fonts/MaterialIcons-Regular'),
|
||||
src: url(fonts/MaterialIcons-Regular.eot); /* For IE6-8 */
|
||||
src: local('Material Icons'),
|
||||
local('MaterialIcons-Regular'),
|
||||
url(fonts/MaterialIcons-Regular.woff2) format('woff2'),
|
||||
url(fonts/MaterialIcons-Regular.woff) format('woff'),
|
||||
url(fonts/MaterialIcons-Regular.ttf) format('truetype');
|
||||
|
||||
@@ -22,6 +22,30 @@ function Http() {
|
||||
this._headers = {};
|
||||
}
|
||||
|
||||
Http.prototype.getContentDigest = function(cb) {
|
||||
if (this.oReq.hasHeader('Docker-Content-Digest')) {
|
||||
// Same origin or advanced CORS headers set:
|
||||
// 'Access-Control-Expose-Headers: Docker-Content-Digest'
|
||||
cb(this.oReq.getResponseHeader('Docker-Content-Digest'))
|
||||
} else if (window.crypto && window.TextEncoder) {
|
||||
crypto.subtle.digest(
|
||||
'SHA-256',
|
||||
new TextEncoder().encode(this.oReq.responseText)
|
||||
).then(function (buffer) {
|
||||
cb(
|
||||
'sha256:' + Array.from(
|
||||
new Uint8Array(buffer)
|
||||
).map(function(byte) {
|
||||
return byte.toString(16).padStart(2, '0');
|
||||
}).join('')
|
||||
);
|
||||
})
|
||||
} else {
|
||||
// IE and old Edge
|
||||
// simply do not call the callback and skip the setup downstream
|
||||
}
|
||||
};
|
||||
|
||||
Http.prototype.addEventListener = function(e, f) {
|
||||
this._events[e] = f;
|
||||
const self = this;
|
||||
|
||||
@@ -18,7 +18,7 @@ var registryUI = {}
|
||||
registryUI.URL_QUERY_PARAM_REGEX = /[&?]url=/;
|
||||
registryUI.URL_PARAM_REGEX = /^url=/;
|
||||
|
||||
registryUI.name = registryUI.url = function(byPassQueryParam) {
|
||||
registryUI.url = function(byPassQueryParam) {
|
||||
if (!registryUI._url) {
|
||||
const url = registryUI.getUrlQueryParam();
|
||||
if (url) {
|
||||
@@ -33,6 +33,9 @@ registryUI.name = registryUI.url = function(byPassQueryParam) {
|
||||
}
|
||||
return registryUI._url;
|
||||
}
|
||||
registryUI.name = function() {
|
||||
return registryUI.stripHttps(registryUI.url());
|
||||
}
|
||||
registryUI.getRegistryServer = function(i) {
|
||||
try {
|
||||
const res = JSON.parse(localStorage.getItem('registryServer'));
|
||||
@@ -85,7 +88,7 @@ registryUI.removeServer = function(url) {
|
||||
}
|
||||
|
||||
registryUI.updateHistory = function(url) {
|
||||
history.pushState(null, '', (url ? '?url=' + registryUI.encodeURI(url) : '?') + window.location.hash);
|
||||
registryUI.updateQueryString({ url: registryUI.encodeURI(url) })
|
||||
registryUI._url = url;
|
||||
}
|
||||
|
||||
@@ -100,10 +103,12 @@ registryUI.getUrlQueryParam = function () {
|
||||
};
|
||||
|
||||
registryUI.encodeURI = function(url) {
|
||||
if (!url) { return; }
|
||||
return url.indexOf('&') < 0 ? window.encodeURIComponent(url) : btoa(url);
|
||||
};
|
||||
|
||||
registryUI.decodeURI = function(url) {
|
||||
if (!url) { return; }
|
||||
return url.startsWith('http') ? window.decodeURIComponent(url) : atob(url);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,10 +16,20 @@
|
||||
*/
|
||||
var registryUI = {}
|
||||
registryUI.url = function() {
|
||||
return '${URL}';
|
||||
var url = '${URL}';
|
||||
if (!url) {
|
||||
url = window.location.origin + window.location.pathname;
|
||||
return url.endsWith('/') ? url.substr(0, url.length - 1) : url;
|
||||
}
|
||||
return url;
|
||||
};
|
||||
registryUI.name = function() {
|
||||
return '${REGISTRY_TITLE}' || registryUI.url();
|
||||
const name = '${REGISTRY_TITLE}';
|
||||
if (name) {
|
||||
// the user can strip the http prefix if they wish
|
||||
return name;
|
||||
}
|
||||
return registryUI.stripHttps(registryUI.url());
|
||||
};
|
||||
registryUI.pullUrl = '${PULL_URL}';
|
||||
registryUI.isImageRemoveActivated = true;
|
||||
|
||||
121
src/scripts/utils.js
Normal file
121
src/scripts/utils.js
Normal file
@@ -0,0 +1,121 @@
|
||||
|
||||
registryUI.bytesToSize = function (bytes) {
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes == undefined || isNaN(bytes)) {
|
||||
return '?';
|
||||
} else if (bytes == 0) {
|
||||
return '0 Byte';
|
||||
}
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
return Math.ceil(bytes / Math.pow(1024, i)) + ' ' + sizes[i];
|
||||
};
|
||||
|
||||
registryUI.dateFormat = function(date) {
|
||||
if (date === undefined) {
|
||||
return '';
|
||||
}
|
||||
const labels = ['a second', 'seconds', 'a minute', 'minutes', 'an hour', 'hours', 'a day', 'days', 'a month', 'months', 'a year', 'years'];
|
||||
const maxSeconds = [1, 60, 3600, 86400, 2592000, 31104000, Infinity];
|
||||
const diff = (new Date() - date) / 1000;
|
||||
for (var i = 0; i < maxSeconds.length - 1; i++) {
|
||||
if (maxSeconds[i] * 2 >= diff) {
|
||||
return labels[i * 2];
|
||||
} else if (maxSeconds[i + 1] > diff) {
|
||||
return Math.floor(diff / maxSeconds[i]) + ' ' + labels[i * 2 + 1];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
registryUI.getHistoryIcon = function(attribute) {
|
||||
switch (attribute) {
|
||||
case 'architecture':
|
||||
return 'memory';
|
||||
case 'created':
|
||||
return 'event';
|
||||
case 'docker_version':
|
||||
return '';
|
||||
case 'os':
|
||||
return 'developer_board';
|
||||
case 'Cmd':
|
||||
return 'launch';
|
||||
case 'Entrypoint':
|
||||
return 'input';
|
||||
case 'Env':
|
||||
return 'notes';
|
||||
case 'Labels':
|
||||
return 'label';
|
||||
case 'User':
|
||||
return 'face';
|
||||
case 'Volumes':
|
||||
return 'storage';
|
||||
case 'WorkingDir':
|
||||
return 'home';
|
||||
case 'author':
|
||||
return 'account_circle';
|
||||
case 'id':
|
||||
case 'digest':
|
||||
return 'settings_ethernet';
|
||||
case 'created_by':
|
||||
return 'build';
|
||||
case 'size':
|
||||
return 'get_app';
|
||||
case 'ExposedPorts':
|
||||
return 'router';
|
||||
default:
|
||||
''
|
||||
}
|
||||
}
|
||||
|
||||
registryUI.getPage = function(elts, page, limit) {
|
||||
if (!limit) { limit = 100; }
|
||||
if (!elts) { return []; }
|
||||
return elts.slice((page - 1) * limit, limit * page);
|
||||
}
|
||||
|
||||
registryUI.getNumPages = function(elts, limit) {
|
||||
if (!limit) { limit = 100; }
|
||||
if (!elts) { return 0; }
|
||||
return Math.trunc(elts.length / limit) + 1;
|
||||
}
|
||||
|
||||
registryUI.getPageLabels = function(page, nPages) {
|
||||
var pageLabels = [];
|
||||
var maxItems = 10;
|
||||
if (nPages === 1) { return pageLabels; }
|
||||
if (page !== 1 && nPages >= maxItems) {
|
||||
pageLabels.push({'icon': 'first_page', page: 1});
|
||||
pageLabels.push({'icon': 'chevron_left', page: page - 1});
|
||||
}
|
||||
var start = Math.round(Math.max(1, Math.min(page - maxItems / 2, nPages - maxItems + 1)));
|
||||
for (var i = start; i < Math.min(nPages + 1, start + maxItems); i++) {
|
||||
pageLabels.push({
|
||||
page: i,
|
||||
current: i === page,
|
||||
'space-left': page === 1 && nPages > maxItems,
|
||||
'space-right': page === nPages && nPages > maxItems
|
||||
});
|
||||
}
|
||||
if (page !== nPages && nPages >= maxItems) {
|
||||
pageLabels.push({'icon': 'chevron_right', page: page + 1});
|
||||
pageLabels.push({'icon': 'last_page', page: nPages});
|
||||
}
|
||||
return pageLabels;
|
||||
}
|
||||
|
||||
registryUI.updateQueryString = function(qs) {
|
||||
var search = '';
|
||||
for (var key in qs) {
|
||||
if (qs[key] !== undefined) {
|
||||
search += (search.length > 0 ? '&' : '?') +key + '=' + qs[key];
|
||||
}
|
||||
}
|
||||
history.pushState(null, '', search + window.location.hash);
|
||||
}
|
||||
|
||||
registryUI.stripHttps = function (url) {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
return url.replace(/^https?:\/\//, '');
|
||||
};
|
||||
147
src/style.css
147
src/style.css
@@ -48,14 +48,31 @@ main {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
material-card {
|
||||
min-height: 200px;
|
||||
max-width: 75%;
|
||||
material-card, pagination .conatianer {
|
||||
max-width: 95%;
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
pagination .conatianer {
|
||||
display: flex;
|
||||
display: -moz-flex;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
}
|
||||
|
||||
pagination .conatianer .pagination-centered {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
/* 1515px * 0.95 = 1440px */
|
||||
@media screen and (min-width: 1515px){
|
||||
material-card, pagination .conatianer {
|
||||
max-width: 1440px;
|
||||
}
|
||||
}
|
||||
|
||||
material-spinner {
|
||||
align-self: center;
|
||||
}
|
||||
@@ -88,6 +105,7 @@ h2 {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.material-card-title-action h2 .source-hint,
|
||||
.material-card-title-action h2 .item-count {
|
||||
font-size: 0.7em;
|
||||
margin-left: 1em;
|
||||
@@ -205,12 +223,14 @@ material-card table th {
|
||||
}
|
||||
|
||||
material-card material-button:hover,
|
||||
material-card table tbody tr:hover {
|
||||
material-card table tbody tr:hover,
|
||||
pagination material-button:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
material-card material-button,
|
||||
material-card table tbody tr {
|
||||
material-card table tbody tr,
|
||||
pagination material-button {
|
||||
transition-duration: .28s;
|
||||
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
|
||||
transition-property: background-color;
|
||||
@@ -319,7 +339,8 @@ dropdown-item, #menu-control-dropdown p {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
material-popup material-button {
|
||||
material-popup material-button,
|
||||
pagination material-button {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
@@ -330,6 +351,7 @@ material-popup material-button:hover material-waves {
|
||||
|
||||
material-popup .popup {
|
||||
max-width: 450px;
|
||||
top: 2em;
|
||||
}
|
||||
|
||||
footer {
|
||||
@@ -339,6 +361,22 @@ footer {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-height: 750px) {
|
||||
main {
|
||||
min-height: calc(100% - 164px - 2.5em);
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
footer {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 760px) and (max-height: 750px) {
|
||||
main {
|
||||
min-height: calc(100% - 144px - 2.5em);
|
||||
}
|
||||
}
|
||||
|
||||
.select-padding {
|
||||
padding: 20px 0;
|
||||
}
|
||||
@@ -361,7 +399,27 @@ select {
|
||||
}
|
||||
|
||||
.copy-to-clipboard {
|
||||
padding: 12px 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
#image-tag-header {
|
||||
/* spacing for clipboard + default th spacing */
|
||||
/* 5 + 2 + 3 + 24 + 3 + 2 + 18 */
|
||||
padding-right: 57px;
|
||||
}
|
||||
image-tag, .copy-to-clipboard {
|
||||
display: inline-block;
|
||||
}
|
||||
image-content-digest {
|
||||
display: none;
|
||||
font-family: 'Roboto Mono', 'Roboto', 'Helvetica', 'Arial', sans-serif !important;
|
||||
}
|
||||
@media screen and (min-width: 1024px) {
|
||||
#image-content-digest-header {
|
||||
padding-right: 57px;
|
||||
}
|
||||
image-content-digest {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.show-tag-history {
|
||||
@@ -371,10 +429,14 @@ select {
|
||||
|
||||
.remove-tag {
|
||||
padding: 12px 5px;
|
||||
width: 30px;
|
||||
width: 66px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.remove-tag.delete {
|
||||
padding: 7px 5px;
|
||||
}
|
||||
|
||||
catalog material-card,
|
||||
tag-history material-card {
|
||||
min-height: auto;
|
||||
@@ -417,7 +479,8 @@ tag-history-button button {
|
||||
border: none;
|
||||
}
|
||||
|
||||
material-card material-button {
|
||||
material-card material-button,
|
||||
pagination material-button {
|
||||
max-height: 30px;
|
||||
max-width: 30px;
|
||||
}
|
||||
@@ -426,7 +489,8 @@ material-button:hover material-waves {
|
||||
background: none;
|
||||
}
|
||||
|
||||
material-card material-button {
|
||||
material-card material-button,
|
||||
pagination material-button {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
@@ -445,4 +509,67 @@ catalog-element catalog-element.showing material-card,
|
||||
catalog-element catalog-element.hide material-card {
|
||||
margin-top: -50px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
catalog-element catalog-element .list > span i.material-icons {
|
||||
margin-right: 48px;
|
||||
}
|
||||
|
||||
remove-image {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
material-checkbox .label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
taglist material-checkbox {
|
||||
margin: auto;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
material-checkbox.indeterminate .checkbox .checkmark {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
material-checkbox.indeterminate .checkbox.checked .checkmark {
|
||||
transform: rotate(90deg) scale(1);
|
||||
-webkit-transform: rotate(90deg) scale(1);
|
||||
-ms-transform: rotate(90deg) scale(1);
|
||||
-moz-transform: rotate(90deg) scale(1);
|
||||
-o-transform: rotate(90deg) scale(1);
|
||||
}
|
||||
|
||||
material-checkbox .checkbox {
|
||||
border-color: #777;
|
||||
}
|
||||
|
||||
material-checkbox .checkbox.checked {
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
pagination material-button {
|
||||
padding: 0.2em 0.75em;
|
||||
}
|
||||
|
||||
pagination material-button .content {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
line-height: 1.9em;
|
||||
}
|
||||
|
||||
pagination material-button.current {
|
||||
border: 1px solid rgba(0, 0, 0, .12);
|
||||
}
|
||||
|
||||
pagination material-button.current.space-left {
|
||||
margin-left: 85px;
|
||||
}
|
||||
|
||||
pagination material-button.current.space-right {
|
||||
margin-right: 85px;
|
||||
}
|
||||
|
||||
pagination material-button .content i.material-icons {
|
||||
color: #000;
|
||||
}
|
||||
@@ -92,12 +92,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
registryUI.errorSnackbar = function(message) {
|
||||
return registryUI.snackbar(message, true);
|
||||
};
|
||||
registryUI.showErrorCanNotReadContentDigest = function() {
|
||||
registryUI.errorSnackbar(
|
||||
'Access on registry response was blocked. Try adding the header ' +
|
||||
'`Access-Control-Expose-Headers: Docker-Content-Digest`' +
|
||||
' to your proxy or registry: ' +
|
||||
'https://docs.docker.com/registry/configuration/#http'
|
||||
);
|
||||
};
|
||||
registryUI.cleanName = function() {
|
||||
const url = registryUI.pullUrl || (registryUI.url() && registryUI.url().length > 0 && registryUI.url()) || window.location.host;
|
||||
if (url) {
|
||||
return url.startsWith('http') ? url.replace(/https?:\/\//, '') : url;
|
||||
}
|
||||
return '';
|
||||
return registryUI.stripHttps(url);
|
||||
};
|
||||
route.parser(null, function(path, filter) {
|
||||
const f = filter
|
||||
@@ -116,6 +121,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
registryUI.DockerImage = function(name, tag) {
|
||||
this.name = name;
|
||||
this.tag = tag;
|
||||
this.chars = 0;
|
||||
riot.observable(this);
|
||||
this.on('get-size', function() {
|
||||
if (this.size !== undefined) {
|
||||
@@ -130,8 +136,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
return this.fillInfo();
|
||||
});
|
||||
this.on('get-date', function() {
|
||||
if (this.date !== undefined) {
|
||||
return this.trigger('date', this.date);
|
||||
if (this.creationDate !== undefined) {
|
||||
return this.trigger('creation-date', this.creationDate);
|
||||
}
|
||||
return this.fillInfo();
|
||||
});
|
||||
this.on('content-digest-chars', function (chars) {
|
||||
this.chars = chars;
|
||||
});
|
||||
this.on('get-content-digest-chars', function() {
|
||||
return this.trigger('content-digest-chars', this.chars);
|
||||
});
|
||||
this.on('get-content-digest', function() {
|
||||
if (this.digest !== undefined) {
|
||||
return this.trigger('content-digest', this.digest);
|
||||
}
|
||||
return this.fillInfo();
|
||||
});
|
||||
@@ -181,6 +199,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
self.layers = response.layers;
|
||||
self.trigger('size', self.size);
|
||||
self.trigger('sha256', self.sha256);
|
||||
oReq.getContentDigest(function (digest) {
|
||||
self.digest = digest;
|
||||
self.trigger('content-digest', digest);
|
||||
});
|
||||
self.getBlobs(response.config.digest)
|
||||
} else if (this.status == 404) {
|
||||
registryUI.errorSnackbar('Manifest for ' + self.name + ':' + self.tag + ' not found');
|
||||
@@ -189,7 +211,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
});
|
||||
oReq.open('GET', registryUI.url() + '/v2/' + self.name + '/manifests/' + self.tag);
|
||||
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
|
||||
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json');
|
||||
oReq.send();
|
||||
};
|
||||
|
||||
@@ -217,25 +239,38 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
});
|
||||
oReq.open('GET', registryUI.url() + '/v2/' + self.name + '/blobs/' + blob);
|
||||
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
|
||||
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json');
|
||||
oReq.send();
|
||||
};
|
||||
|
||||
registryUI.bytesToSize = function (bytes) {
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes == undefined || isNaN(bytes)) {
|
||||
return '?';
|
||||
} else if (bytes == 0) {
|
||||
return '0 Byte';
|
||||
}
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
return Math.ceil(bytes / Math.pow(1024, i)) + ' ' + sizes[i];
|
||||
};
|
||||
|
||||
registryUI.taglist.go = function(image) {
|
||||
route('taglist/' + image);
|
||||
};
|
||||
|
||||
registryUI.getPageQueryParam = function() {
|
||||
var qs = route.query();
|
||||
try {
|
||||
return qs.page !== undefined ? parseInt(qs.page.replace(/#.*/, '')) : 1;
|
||||
} catch(e) { return 1; }
|
||||
}
|
||||
|
||||
registryUI.getQueryParams = function(update) {
|
||||
var qs = route.query();
|
||||
update = update || {};
|
||||
for (var key in qs) {
|
||||
if (qs[key] !== undefined) {
|
||||
qs[key] = qs[key].replace(/#!.*/, '');
|
||||
} else {
|
||||
delete qs[key];
|
||||
}
|
||||
}
|
||||
for (var key in update) {
|
||||
if (update[key] !== undefined) {
|
||||
qs[key] = update[key];
|
||||
}
|
||||
}
|
||||
return qs;
|
||||
}
|
||||
route.start(true);
|
||||
</script>
|
||||
</app>
|
||||
@@ -21,13 +21,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<span>
|
||||
<i class="material-icons">send</i>
|
||||
{ typeof opts.item === "string" ? opts.item : opts.item.repo }
|
||||
<div hide="{typeof opts.item === "string"}" class="item-count right">
|
||||
<div if="{typeof opts.item !== "string"}" class="item-count right">
|
||||
{ opts.item.images && opts.item.images.length } images
|
||||
<i class="material-icons animated {expanded: opts.expanded}">expand_more</i>
|
||||
</div>
|
||||
</span>
|
||||
</material-card>
|
||||
<catalog-element hide="{typeof opts.item === "string"}" class="animated {hide: !expanded, expanding: expanding}" each="{item in item.images}" />
|
||||
<catalog-element if="{typeof opts.item !== "string"}" class="animated {hide: !expanded, expanding: expanding}" each="{item in item.images}" />
|
||||
<script>
|
||||
this.on('mount', function() {
|
||||
const self = this;
|
||||
|
||||
@@ -16,17 +16,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<catalog>
|
||||
<!-- Begin of tag -->
|
||||
<material-card ref="catalog-tag" class="catalog">
|
||||
<material-card ref="catalog-tag" class="catalog header">
|
||||
<div class="material-card-title-action">
|
||||
<h2>
|
||||
Repositories of { registryUI.name() }
|
||||
<div class="item-count">{ registryUI.catalog.length } images</div>
|
||||
</h2>
|
||||
</div>
|
||||
<div hide="{ registryUI.catalog.loadend }" class="spinner-wrapper">
|
||||
<material-spinner></material-spinner>
|
||||
</div>
|
||||
</material-card>
|
||||
<div hide="{ registryUI.catalog.loadend }" class="spinner-wrapper">
|
||||
<material-spinner></material-spinner>
|
||||
</div>
|
||||
<catalog-element each="{ item in registryUI.catalog.repositories }" />
|
||||
<script>
|
||||
registryUI.catalog.instance = this;
|
||||
@@ -36,6 +36,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
oReq.addEventListener('load', function() {
|
||||
registryUI.catalog.repositories = [];
|
||||
if (this.status == 200) {
|
||||
if (!registryUI.url()) {
|
||||
registryUI._url = window.location.origin + window.location.pathname.replace(/\/+$/, '')
|
||||
}
|
||||
registryUI.catalog.repositories = JSON.parse(this.responseText).repositories || [];
|
||||
registryUI.catalog.repositories.sort();
|
||||
registryUI.catalog.length = registryUI.catalog.repositories.length; registryUI.catalog.repositories = registryUI.catalog.repositories.reduce(function(acc, e) {
|
||||
|
||||
@@ -15,13 +15,29 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<copy-to-clipboard>
|
||||
<div class="copy-to-clipboard">
|
||||
<input ref="input" style="display: none; width: 1px; height: 1px;" value="{ this.dockerCmd }">
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd" onclick="{ this.copy }" title="Copy pull command.">
|
||||
<i class="material-icons">content_copy</i>
|
||||
</material-button>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
this.dockerCmd = 'docker pull ' + registryUI.cleanName() + '/' + opts.image.name + ':' + opts.image.tag;
|
||||
this.prefix = 'docker pull ' + registryUI.cleanName() + '/' + opts.image.name;
|
||||
const self = this;
|
||||
if (opts.target === 'tag') {
|
||||
self.dockerCmd = self.prefix + ':' + opts.image.tag;
|
||||
} else {
|
||||
opts.image.one('content-digest', function (digest) {
|
||||
self.dockerCmd = self.prefix + '@' + digest;
|
||||
});
|
||||
opts.image.trigger('get-content-digest');
|
||||
}
|
||||
|
||||
this.copy = function () {
|
||||
if (!self.dockerCmd) {
|
||||
registryUI.showErrorCanNotReadContentDigest();
|
||||
return;
|
||||
}
|
||||
const copyText = this.refs['input'];
|
||||
copyText.style.display = 'block';
|
||||
copyText.select();
|
||||
|
||||
48
src/tags/image-content-digest.tag
Normal file
48
src/tags/image-content-digest.tag
Normal file
@@ -0,0 +1,48 @@
|
||||
<!--
|
||||
Copyright (C) 2019 Jakob Ackermann
|
||||
|
||||
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-content-digest>
|
||||
<div title="{ this.title }">{ this.display_id }</div>
|
||||
<script type="text/javascript">
|
||||
const self = this;
|
||||
self.chars = -1;
|
||||
|
||||
self.onResize = function(chars) {
|
||||
if (chars === self.chars) {
|
||||
return;
|
||||
}
|
||||
self.chars = chars;
|
||||
if (chars >= 70) {
|
||||
self.display_id = self.digest;
|
||||
self.title = '';
|
||||
} else if (chars === 0) {
|
||||
self.display_id = '';
|
||||
self.title = self.digest;
|
||||
} else {
|
||||
self.display_id = self.digest.slice(0, chars) + '...';
|
||||
self.title = self.digest;
|
||||
}
|
||||
self.update();
|
||||
};
|
||||
|
||||
opts.image.one('content-digest', function(digest) {
|
||||
self.digest = digest;
|
||||
opts.image.on('content-digest-chars', self.onResize);
|
||||
opts.image.trigger('get-content-digest-chars');
|
||||
});
|
||||
opts.image.trigger('get-content-digest');
|
||||
</script>
|
||||
</image-content-digest>
|
||||
@@ -15,29 +15,16 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<image-date>
|
||||
<div title="Creation date { this.localDate }">{ this.dateFormat(this.date) } ago</div>
|
||||
<div title="Creation date { this.localDate }">{ registryUI.dateFormat(this.date) } ago</div>
|
||||
<script type="text/javascript">
|
||||
const self = this;
|
||||
this.dateFormat = function(date) {
|
||||
if (date === undefined) {
|
||||
return '';
|
||||
}
|
||||
const labels = ['a second', 'seconds', 'a minute', 'minutes', 'an hour', 'hours', 'a day', 'days', 'a month', 'months', 'a year', 'years'];
|
||||
const maxSeconds = [1, 60, 3600, 86400, 2592000, 31104000, Infinity];
|
||||
const diff = (new Date() - date) / 1000;
|
||||
for (var i = 0; i < maxSeconds.length - 1; i++) {
|
||||
if (maxSeconds[i] * 2 >= diff) {
|
||||
return labels[i * 2];
|
||||
} else if (maxSeconds[i + 1] > diff) {
|
||||
return Math.floor(diff / maxSeconds[i]) + ' ' + labels[i * 2 + 1];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
opts.image.on('creation-date', function(date) {
|
||||
self.date = date;
|
||||
self.localDate = date.toLocaleString()
|
||||
self.update();
|
||||
});
|
||||
|
||||
opts.image.trigger('get-date');
|
||||
</script>
|
||||
</image-date>
|
||||
39
src/tags/pagination.tag
Normal file
39
src/tags/pagination.tag
Normal file
@@ -0,0 +1,39 @@
|
||||
<!--
|
||||
Copyright (C) 2016-2019 Jones Magloire @Joxit
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<pagination>
|
||||
<!-- Begin of tag -->
|
||||
<div class="conatianer">
|
||||
<div class="pagination-centered">
|
||||
<material-button waves-color="rgba(158,158,158,.4)" each="{p in this.opts.pages}" class="{ current: p.current, space-left: p['space-left'], space-right: p['space-right']}">
|
||||
<i show="{ p.icon }" class="material-icons">{ p.icon }</i>
|
||||
<div hide="{ p.icon }">{ p.page }</div>
|
||||
</material-button>
|
||||
</div>
|
||||
<div>
|
||||
<script>
|
||||
this.on('updated', function() {
|
||||
if (!this.tags['material-button']) { return; }
|
||||
var buttons = Array.isArray(this.tags['material-button']) ? this.tags['material-button'] : [this.tags['material-button']];
|
||||
buttons.forEach(function(button) {
|
||||
button.root.onclick = function() {
|
||||
registryUI.taglist.instance.trigger('page-update', button.p.page)
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!-- End of tag -->
|
||||
</pagination>
|
||||
@@ -15,51 +15,63 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<remove-image>
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd" title="This will delete the image.">
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd" title="This will delete the image." if="{ !opts.multiDelete }">
|
||||
<i class="material-icons">delete</i>
|
||||
</material-button>
|
||||
<material-checkbox if="{ opts.multiDelete }" title="Select this tag to delete it."></material-checkbox>
|
||||
<script type="text/javascript">
|
||||
const self = this;
|
||||
this.on('mount', function() {
|
||||
this.tags['material-button'].root.onclick = function() {
|
||||
const name = self.opts.image.name;
|
||||
const tag = self.opts.image.tag;
|
||||
const oReq = new Http();
|
||||
oReq.addEventListener('loadend', function() {
|
||||
registryUI.taglist.go(name);
|
||||
if (this.status == 200) {
|
||||
if (!this.hasHeader('Docker-Content-Digest')) {
|
||||
registryUI.errorSnackbar('You need to add Access-Control-Expose-Headers: [\'Docker-Content-Digest\'] in your server configuration.');
|
||||
return;
|
||||
}
|
||||
const digest = this.getResponseHeader('Docker-Content-Digest');
|
||||
const oReq = new Http();
|
||||
oReq.addEventListener('loadend', function() {
|
||||
if (this.status == 200 || this.status == 202) {
|
||||
registryUI.taglist.display()
|
||||
registryUI.snackbar('Deleting ' + name + ':' + tag + ' image. Run `registry garbage-collect config.yml` on your registry');
|
||||
} else if (this.status == 404) {
|
||||
registryUI.errorSnackbar('Digest not found');
|
||||
} else {
|
||||
registryUI.snackbar(this.responseText);
|
||||
}
|
||||
});
|
||||
oReq.open('DELETE', registryUI.url() + '/v2/' + name + '/manifests/' + digest);
|
||||
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
|
||||
oReq.addEventListener('error', function() {
|
||||
registryUI.errorSnackbar('An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: [\'DELETE\'].');
|
||||
});
|
||||
oReq.send();
|
||||
} else if (this.status == 404) {
|
||||
registryUI.errorSnackbar('Manifest for ' + name + ':' + tag + ' not found');
|
||||
} else {
|
||||
registryUI.snackbar(this.responseText);
|
||||
}
|
||||
});
|
||||
oReq.open('HEAD', registryUI.url() + '/v2/' + name + '/manifests/' + tag);
|
||||
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
|
||||
oReq.send();
|
||||
};
|
||||
|
||||
this.on('updated', function() {
|
||||
});
|
||||
|
||||
this.on('updated', function() {
|
||||
if (self.multiDelete == self.opts.multiDelete) {
|
||||
return;
|
||||
}
|
||||
if (this.tags['material-button']) {
|
||||
this.delete = this.tags['material-button'].root.onclick = function(ignoreError) {
|
||||
const name = self.opts.image.name;
|
||||
const tag = self.opts.image.tag;
|
||||
registryUI.taglist.go(name);
|
||||
if (!self.digest) {
|
||||
registryUI.showErrorCanNotReadContentDigest();
|
||||
return;
|
||||
}
|
||||
const oReq = new Http();
|
||||
oReq.addEventListener('loadend', function() {
|
||||
if (this.status == 200 || this.status == 202) {
|
||||
registryUI.taglist.display()
|
||||
registryUI.snackbar('Deleting ' + name + ':' + tag + ' image. Run `registry garbage-collect config.yml` on your registry');
|
||||
} else if (this.status == 404) {
|
||||
ignoreError || registryUI.errorSnackbar('Digest not found');
|
||||
} else {
|
||||
registryUI.snackbar(this.responseText);
|
||||
}
|
||||
});
|
||||
oReq.open('DELETE', registryUI.url() + '/v2/' + name + '/manifests/' + self.digest);
|
||||
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json');
|
||||
oReq.addEventListener('error', function() {
|
||||
registryUI.errorSnackbar('An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: [\'DELETE\'].');
|
||||
});
|
||||
oReq.send();
|
||||
};
|
||||
}
|
||||
|
||||
if (this.tags['material-checkbox']) {
|
||||
if (!this.opts.multiDelete && this.tags['material-checkbox'].checked) {
|
||||
this.tags['material-checkbox'].toggle();
|
||||
}
|
||||
this.tags['material-checkbox'].on('toggle', function() {
|
||||
registryUI.taglist.instance.trigger('toggle-remove-image', this.checked);
|
||||
});
|
||||
}
|
||||
self.multiDelete = self.opts.multiDelete;
|
||||
});
|
||||
|
||||
opts.image.one('content-digest', function(digest) {
|
||||
self.digest = digest;
|
||||
});
|
||||
opts.image.trigger('get-content-digest');
|
||||
</script>
|
||||
</remove-image>
|
||||
|
||||
@@ -15,50 +15,9 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<tag-history-element class="{entry.key}">
|
||||
<div class="headline"><i class="material-icons">{ this.getIcon(entry.key) }</i>
|
||||
<div class="headline"><i class="material-icons">{ registryUI.getHistoryIcon(entry.key) }</i>
|
||||
<p>{ entry.key.replace('_', ' ') }</p>
|
||||
</div>
|
||||
<div class="value" if={!(entry.value instanceof Array)}> { entry.value }</div>
|
||||
<div class="value" each={ e in entry.value } if={entry.value instanceof Array}> { e }</div>
|
||||
<script type="text/javascript">
|
||||
this.getIcon = function(attribute) {
|
||||
switch (attribute) {
|
||||
case 'architecture':
|
||||
return 'memory';
|
||||
case 'created':
|
||||
return 'event';
|
||||
case 'docker_version':
|
||||
return '';
|
||||
case 'os':
|
||||
return 'developer_board';
|
||||
case 'Cmd':
|
||||
return 'launch';
|
||||
case 'Entrypoint':
|
||||
return 'input';
|
||||
case 'Env':
|
||||
return 'notes';
|
||||
case 'Labels':
|
||||
return 'label';
|
||||
case 'User':
|
||||
return 'face';
|
||||
case 'Volumes':
|
||||
return 'storage';
|
||||
case 'WorkingDir':
|
||||
return 'home';
|
||||
case 'author':
|
||||
return 'account_circle';
|
||||
case 'id':
|
||||
case 'digest':
|
||||
return 'settings_ethernet';
|
||||
case 'created_by':
|
||||
return 'build';
|
||||
case 'size':
|
||||
return 'get_app';
|
||||
case 'ExposedPorts':
|
||||
return 'router';
|
||||
default:
|
||||
''
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</tag-history-element>
|
||||
@@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<tag-history>
|
||||
<material-card ref="tag-history-tag" class="tag-history">
|
||||
<material-card ref="tag-history-tag" class="tag-history header">
|
||||
<div class="material-card-title-action">
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
@@ -95,7 +95,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
const processBlobs = function(blobs) {
|
||||
function exec(elt) {
|
||||
const guiElements = [];
|
||||
for (const attribute in elt) {
|
||||
for (var attribute in elt) {
|
||||
if (elt.hasOwnProperty(attribute) && attribute != 'empty_layer') {
|
||||
const value = elt[attribute];
|
||||
const guiElement = {
|
||||
|
||||
@@ -16,62 +16,174 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<taglist>
|
||||
<!-- Begin of tag -->
|
||||
<material-card ref="taglist-tag" class="taglist">
|
||||
<div class="material-card-title-action">
|
||||
<material-card class="header">
|
||||
<div class="material-card-title-action ">
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd" onclick="registryUI.home();">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</material-button>
|
||||
<h2>
|
||||
Tags of { registryUI.name() + '/' + registryUI.taglist.name }
|
||||
Tags of { registryUI.taglist.name }
|
||||
<div class="source-hint">
|
||||
Sourced from { registryUI.name() + '/' + registryUI.taglist.name }
|
||||
</div>
|
||||
<div class="item-count">{ registryUI.taglist.tags.length } tags</div>
|
||||
</h2>
|
||||
</div>
|
||||
<div hide="{ registryUI.taglist.loadend }" class="spinner-wrapper">
|
||||
<material-spinner></material-spinner>
|
||||
</div>
|
||||
</material-card>
|
||||
<div hide="{ registryUI.taglist.loadend }" class="spinner-wrapper">
|
||||
<material-spinner></material-spinner>
|
||||
</div>
|
||||
<pagination pages="{ registryUI.getPageLabels(this.page, registryUI.getNumPages(registryUI.taglist.tags)) }"></pagination>
|
||||
<material-card ref="taglist-tag" class="taglist"
|
||||
multi-delete={ this.multiDelete }
|
||||
tags={ registryUI.getPage(registryUI.taglist.tags, this.page) }
|
||||
show="{ registryUI.taglist.loadend }">
|
||||
<table show="{ registryUI.taglist.loadend }" style="border: none;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="material-card-th-left">Repository</th>
|
||||
<th></th>
|
||||
<th>Creation date</th>
|
||||
<th>Size</th>
|
||||
<th id="image-content-digest-header">Content Digest</th>
|
||||
|
||||
<th
|
||||
id="image-tag-header"
|
||||
class="{ registryUI.taglist.asc ? 'material-card-th-sorted-ascending' : 'material-card-th-sorted-descending' }"
|
||||
onclick="registryUI.taglist.reverse();">Tag
|
||||
</th>
|
||||
<th class="show-tag-history">History</th>
|
||||
<th class="remove-tag" show="{ registryUI.isImageRemoveActivated }"></th>
|
||||
<th class={ 'remove-tag': true, delete: this.parent.toDelete > 0 } if="{ registryUI.isImageRemoveActivated }">
|
||||
<material-checkbox ref="remove-tag-checkbox" class="indeterminate" show={ this.toDelete === 0} title="Toggle multi-delete. Alt+Click to select all tags."></material-checkbox>
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd" title="This will delete selected images." onclick={ registryUI.taglist.bulkDelete } show={ this.toDelete > 0 }>
|
||||
<i class="material-icons">delete</i>
|
||||
</material-button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<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>
|
||||
<tr each="{ image in this.opts.tags }">
|
||||
<td>
|
||||
<image-date image="{ image }"/>
|
||||
</td>
|
||||
<td>
|
||||
<image-size image="{ image }"/>
|
||||
</td>
|
||||
<td>
|
||||
<image-content-digest image="{ image }"/>
|
||||
<copy-to-clipboard target="digest" image={ image }/>
|
||||
</td>
|
||||
<td>
|
||||
<image-tag image="{ image }"/>
|
||||
<copy-to-clipboard target="tag" image={ image }/>
|
||||
</td>
|
||||
<td class="show-tag-history">
|
||||
<tag-history-button image={ image }/>
|
||||
</td>
|
||||
<td show="{ registryUI.isImageRemoveActivated }">
|
||||
<remove-image image={ image }/>
|
||||
<td if="{ registryUI.isImageRemoveActivated }">
|
||||
<remove-image multi-delete={ this.opts.multiDelete } image={ image }/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</material-card>
|
||||
<pagination pages="{ registryUI.getPageLabels(this.page, registryUI.getNumPages(registryUI.taglist.tags)) }"></pagination>
|
||||
<script>
|
||||
registryUI.taglist.instance = this;
|
||||
var self = registryUI.taglist.instance = this;
|
||||
self.page = registryUI.getPageQueryParam();
|
||||
registryUI.taglist.tags = [];
|
||||
const onResize = function() {
|
||||
// window.innerWidth is a blocking access, cache its result.
|
||||
const innerWidth = window.innerWidth;
|
||||
var chars = 0;
|
||||
if (innerWidth >= 1440) {
|
||||
chars = 71;
|
||||
} else if (innerWidth < 1024) {
|
||||
chars = 0;
|
||||
} else {
|
||||
// SHA256:12345678 + scaled between 1024 and 1440px
|
||||
chars = 15 + 56 * ((innerWidth - 1024) / 416);
|
||||
}
|
||||
registryUI.taglist.tags.map(function (image) {
|
||||
image.trigger('content-digest-chars', chars);
|
||||
});
|
||||
};
|
||||
window.addEventListener('resize', onResize);
|
||||
// this may be run before the final document size is available, so schedule
|
||||
// a correction once everything is set up.
|
||||
window.requestAnimationFrame(onResize);
|
||||
|
||||
this.multiDelete = false;
|
||||
this.toDelete = 0;
|
||||
|
||||
this.on('delete', function() {
|
||||
if (!registryUI.isImageRemoveActivated || !this.multiDelete) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
this.on('multi-delete', function() {
|
||||
if (!registryUI.isImageRemoveActivated) {
|
||||
return;
|
||||
}
|
||||
this.multiDelete = !this.multiDelete;
|
||||
});
|
||||
|
||||
this.on('toggle-remove-image', function(checked) {
|
||||
if (checked) {
|
||||
this.toDelete++;
|
||||
} else {
|
||||
this.toDelete--;
|
||||
}
|
||||
|
||||
if (this.toDelete <= 1) {
|
||||
this.update();
|
||||
}
|
||||
});
|
||||
|
||||
this.on('page-update', function(page) {
|
||||
self.page = page < 1 ? 1 : page;
|
||||
registryUI.updateQueryString(registryUI.getQueryParams({ page: self.page }) );
|
||||
this.toDelete = 0;
|
||||
this.update();
|
||||
});
|
||||
|
||||
this._getRemoveImageTags = function() {
|
||||
var images = self.refs['taglist-tag'].tags['remove-image'];
|
||||
if (!(images instanceof Array)) {
|
||||
images = [images];
|
||||
}
|
||||
return images;
|
||||
};
|
||||
|
||||
registryUI.taglist.bulkDelete = function() {
|
||||
if (self.multiDelete && self.toDelete > 0) {
|
||||
self._getRemoveImageTags().filter(function(img) {
|
||||
return img.tags['material-checkbox'].checked;
|
||||
}).forEach(function(img) {
|
||||
img.delete(true);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.on('update', function() {
|
||||
var checkbox = this.refs['taglist-tag'].refs['remove-tag-checkbox'];
|
||||
if (!checkbox || checkbox._toggle) { return; }
|
||||
|
||||
checkbox._toggle = checkbox.toggle;
|
||||
checkbox.toggle = function(e) {
|
||||
if (e.altKey) {
|
||||
self._getRemoveImageTags()
|
||||
.filter(function(img) { return !img.tags['material-checkbox'].checked; })
|
||||
.forEach(function(img) { img.tags['material-checkbox'].toggle() });
|
||||
} else {
|
||||
this._toggle();
|
||||
}
|
||||
};
|
||||
|
||||
checkbox.on('toggle', function() {
|
||||
registryUI.taglist.instance.multiDelete = this.checked;
|
||||
registryUI.taglist.instance.update();
|
||||
});
|
||||
});
|
||||
|
||||
registryUI.taglist.display = function() {
|
||||
registryUI.taglist.tags = [];
|
||||
if (route.routeName == 'taglist') {
|
||||
@@ -80,10 +192,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
oReq.addEventListener('load', function() {
|
||||
registryUI.taglist.tags = [];
|
||||
if (this.status == 200) {
|
||||
registryUI.taglist.tags = JSON.parse(this.responseText).tags || [];
|
||||
registryUI.taglist.tags = registryUI.taglist.tags.map(function(tag) {
|
||||
const tags = JSON.parse(this.responseText).tags || [];
|
||||
registryUI.taglist.tags = tags.map(function(tag) {
|
||||
return new registryUI.DockerImage(registryUI.taglist.name, tag);
|
||||
}).sort(registryUI.DockerImage.compare);
|
||||
window.requestAnimationFrame(onResize);
|
||||
self.trigger('page-update', Math.min(self.page, registryUI.getNumPages(registryUI.taglist.tags)))
|
||||
} else if (this.status == 404) {
|
||||
registryUI.snackbar('Server not found', true);
|
||||
} else {
|
||||
|
||||
@@ -30,6 +30,8 @@ LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
ENV NGINX_PROXY_HEADER_Host '$http_host'
|
||||
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=builder /usr/app/dist/ /usr/share/nginx/html/
|
||||
COPY --from=builder /usr/app/dist/scripts/docker-registry-ui-static.js /usr/share/nginx/html/scripts/docker-registry-ui.js
|
||||
|
||||
Reference in New Issue
Block a user