mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2026-02-17 21:19:51 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8524c0d18b | ||
|
|
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 |
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.
|
||||
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
|
||||
28
CONTRIBUTORS.md
Normal file
28
CONTRIBUTORS.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# 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)
|
||||
- Jernej K. [Cvetk0](https://github.com/Cvetk0)
|
||||
30
README.md
30
README.md
@@ -45,7 +45,26 @@ This web user interface uses [Riot](https://github.com/Riot/riot) the react-like
|
||||
- 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 (see [#89](https://github.com/Joxit/docker-registry-ui/pull/89)) **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
|
||||
|
||||
@@ -120,11 +139,12 @@ docker run -d -p 80:80 joxit/docker-registry-ui
|
||||
|
||||
Some env options are available for use this interface for only one server.
|
||||
|
||||
- `URL`: set the static URL to use (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.
|
||||
|
||||
@@ -158,7 +178,7 @@ If your docker registry need credentials, you will need to send these HEADERS:
|
||||
http:
|
||||
headers:
|
||||
Access-Control-Allow-Origin: ['<your docker-registry-ui url>']
|
||||
Access-Control-Allow-Credentials: true
|
||||
Access-Control-Allow-Credentials: [true]
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS'] # Optional
|
||||
```
|
||||
|
||||
@@ -222,4 +242,4 @@ auth:
|
||||
- [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))
|
||||
- [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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,7 +21,7 @@
|
||||
<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">
|
||||
<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" />
|
||||
|
||||
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"><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>
|
||||
--><!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
@@ -24,7 +24,6 @@ server {
|
||||
#! if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
|
||||
#! return 404;
|
||||
#! }
|
||||
#! proxy_set_header Host $http_host;
|
||||
#! ${NGINX_PROXY_HEADERS}
|
||||
#! proxy_pass ${REGISTRY_URL};
|
||||
#! }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docker-registry-ui",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.3",
|
||||
"scripts": {
|
||||
"build": "./node_modules/gulp/bin/gulp.js build"
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<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" />
|
||||
@@ -58,6 +58,7 @@
|
||||
<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>
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -24,7 +24,12 @@ registryUI.url = function() {
|
||||
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;
|
||||
|
||||
@@ -111,4 +111,11 @@ registryUI.updateQueryString = function(qs) {
|
||||
}
|
||||
}
|
||||
history.pushState(null, '', search + window.location.hash);
|
||||
}
|
||||
}
|
||||
|
||||
registryUI.stripHttps = function (url) {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
return url.replace(/^https?:\/\//, '');
|
||||
};
|
||||
|
||||
@@ -49,7 +49,7 @@ main {
|
||||
}
|
||||
|
||||
material-card, pagination .conatianer {
|
||||
max-width: 75%;
|
||||
max-width: 95%;
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
@@ -66,10 +66,10 @@ pagination .conatianer .pagination-centered {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 950px){
|
||||
material-card {
|
||||
width: 95%;
|
||||
max-width: 750px;
|
||||
/* 1515px * 0.95 = 1440px */
|
||||
@media screen and (min-width: 1515px){
|
||||
material-card, pagination .conatianer {
|
||||
max-width: 1440px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,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;
|
||||
@@ -398,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 {
|
||||
|
||||
@@ -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) {
|
||||
@@ -135,6 +141,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
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();
|
||||
});
|
||||
};
|
||||
|
||||
registryUI.DockerImage._tagReduce = function(acc, e) {
|
||||
@@ -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,7 +239,7 @@ 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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -29,57 +29,52 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
if (self.multiDelete == self.opts.multiDelete) {
|
||||
return;
|
||||
}
|
||||
if (this.tags['material-button']) {
|
||||
this.delete = this.tags['material-button'].root.onclick = function(ignoreError) {
|
||||
if (self.tags['material-button']) {
|
||||
self.delete = 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() {
|
||||
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) {
|
||||
ignoreError || 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();
|
||||
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('Manifest for ' + name + ':' + tag + ' not found');
|
||||
ignoreError || registryUI.errorSnackbar('Digest not found for this image in your registry.');
|
||||
} 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.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();
|
||||
};
|
||||
self.tags['material-button'].root.onclick = function() {
|
||||
self.delete();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tags['material-checkbox']) {
|
||||
if (!this.opts.multiDelete && this.tags['material-checkbox'].checked) {
|
||||
this.tags['material-checkbox'].toggle();
|
||||
if (self.tags['material-checkbox']) {
|
||||
if (!self.opts.multiDelete && self.tags['material-checkbox'].checked) {
|
||||
self.tags['material-checkbox'].toggle();
|
||||
}
|
||||
this.tags['material-checkbox'].on('toggle', function() {
|
||||
registryUI.taglist.instance.trigger('toggle-remove-image', this.checked);
|
||||
self.tags['material-checkbox'].on('toggle', function() {
|
||||
registryUI.taglist.instance.trigger('toggle-remove-image', self.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>
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -22,7 +22,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<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>
|
||||
@@ -38,12 +41,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<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>
|
||||
@@ -57,18 +60,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr each="{ image in this.opts.tags }">
|
||||
<td class="material-card-th-left">{ image.name }</td>
|
||||
<td class="copy-to-clipboard">
|
||||
<copy-to-clipboard image={ image }/>
|
||||
</td>
|
||||
<td>
|
||||
<image-date image="{ image }"/>
|
||||
</td>
|
||||
<td>
|
||||
<image-size image="{ image }"/>
|
||||
</td>
|
||||
<td>
|
||||
<image-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 }/>
|
||||
@@ -84,6 +88,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<script>
|
||||
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;
|
||||
@@ -167,10 +192,11 @@ 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);
|
||||
|
||||
@@ -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