Compare commits

..

28 Commits
2.5.2 ... 2.6.0

Author SHA1 Message Date
Joxit
82f6240dab build: release 2.6.0 🚀 2026-01-20 00:28:17 +01:00
Joxit
21f33f5d4b fix(tags): use Material Symbols instead of Icons 2025-11-11 14:31:03 +01:00
Lukas Engelter
83c33e9d05 feat: add DOCKER_REGISTRY_UI_TITLE and ENABLE_VERSION_NOTIFICATION (#369)
* feature: link title to main view

this way it is possible to go back to the main view, even if two layers deep, i.e. in the history view.

* feature: allow the customization of the title shown in the logo section

we already have REGISTRY_TITLE, but it is used as a reference to where the images come from: "Repositories of …". The UI however does not need to be used in a 1:1 context of the registry. Therefore, the project would benefit from this feature:

We therefore introduce "DOCKER_REGISTRY_UI_TITLE" as a new option, that optionally allows to change the title for more advanced customization and to enable more use cases for this software.

If it is not set, everything will stay the same as prior to this commit and the title will remain "Docker Registry UI"

Note, that the footer will always contain a reference to this project. I think it doesn't need to referenced twice, and makes room for some more customization.

* feature: allow to optionally disable the version check

Note: The default remains to have the version check enabled. It is though sometimes useful to disable it, in the context of customers or open source interests of your software not to bother with version updates to Docker Registry UI.

For example, if you use something like watchtower anyway, there is no need to bother with update notifications to anyone else other then the admin of the server.

* fix: wrongly named environment variable in error template

The environment variables named in the CATALOG_BRANCHING_CONFIGURATION error were wrong.

During that find, i found out that one needs to set both min and max to 0 to disable branching. I tried to make this clear in the README.md file.

* feat: update title and version notification options

Version check done every week instead of every days

---------

Co-authored-by: Joxit <joxit972@gmail.com>
2025-11-08 01:22:51 +01:00
Dmitriy Pertsev
433a2aa17a fix: update for new unprivileged nginx images (#433)
fix #432

---------

Co-authored-by: Joxit <joxit972@gmail.com>
2025-11-07 23:58:31 +01:00
Joxit
3381fb428d feat: add /version.json endpoint 2025-07-20 18:17:43 +02:00
Joxit
7a5e55a2f6 docs: update CORS section 2025-04-04 01:16:35 +02:00
Joxit
06147a8ff0 chore: update dependencies and announce distribution v3 2025-04-03 20:37:35 +02:00
Joxit
a03dd97442 chore: update dependencies 2025-01-18 15:45:39 +01:00
Joxit
3c7429b732 revert: "fix(tag-table): icons for ascending & descending are not shown correctly (#406)"
This reverts commit 9960afe909.
2025-01-16 13:52:26 +01:00
Joxit
68313a1bae docs: update FAQ and default example 2025-01-12 14:11:51 +01:00
Joxit
9960afe909 fix(tag-table): icons for ascending & descending are not shown correctly (#406)
fixes #406
2025-01-11 11:17:25 +01:00
Joxit
22960a2547 chore(confirm-delete): add help icon to link FAQ 2025-01-07 20:32:40 +01:00
Julien-Delavisse
cb776739c2 docs(examples): inconsistency of htpasswd file names with docker compose.yml (read-only-auth example) (#402) 2024-11-28 11:58:30 +01:00
Joxit
b7f732a606 feat(oci): add support to helm exports history and use material symbols 2024-09-13 20:06:14 +02:00
Joxit
079f35976f fix: avoid exceptions and display date when using OCI images (#372)
fixes #372
2024-09-13 20:06:14 +02:00
silverwind
a36e3aac57 docs: improve examples/read-only-auth (#371)
* Improve `examples/read-only-auth`

* Update examples/read-only-auth/nginx.conf
2024-04-05 21:59:24 +02:00
Lukas Engelter
7025df687c feat: add SHOW_TAG_HISTORY option to hide tag history button (#362) 2024-03-14 09:28:54 +01:00
Lukas Engelter
cfbc6e76a8 feat(theme): contrast changes for light and dark themes (#361) 2024-03-12 22:35:45 +01:00
toinux
dc9bdcbedd fix(nginx): too big request header when cookie is defined (#356) 2024-03-10 11:09:21 +01:00
Joxit
6c3c27e215 fix: should notify new version only once a day even when an error occurs (#353)
Add more messages in console

fixes #353
2024-02-08 04:25:14 +01:00
Jones Magloire
6318ccfdf5 docs: delete custom issue template 2023-11-24 18:37:43 +01:00
Christian
686b1709b2 docs: fix a small typo (#345) 2023-10-27 19:51:50 +02:00
Joxit
e79a20a5e5 fix(tag-list): missing argument tagsPerPage for getNumPages function 2023-10-18 23:57:59 +02:00
Joxit
7991442fce fix: DNS name resolving not working in kubernetes (#339)
fixes #339
2023-10-17 02:39:38 +02:00
Joxit
de6d09c98c fix(taglist): refreshing page set all digets to sha256:e3b0c44298... due to missing seesion cache (#337)
fix #337
2023-10-07 12:18:32 +02:00
Joxit
1f2913248e fix(docker): NGINX_PROXY_PASS_URL use long cache DNS resolution (#333)
fix #333
2023-09-23 00:25:28 +02:00
Joxit
3414d7b517 feat(token-auth): check the presence of www-authenticate header before the status code 2023-08-02 23:57:59 +02:00
AJWavio
cd99f6e231 docs: fix registry server configuration example in README (#328, #329) 2023-07-28 22:10:23 +02:00
42 changed files with 287 additions and 178 deletions

View File

@@ -1,10 +0,0 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---

View File

@@ -27,4 +27,4 @@ COPY bin/90-docker-registry-ui.sh /docker-entrypoint.d/90-docker-registry-ui.sh
COPY dist/ /usr/share/nginx/html/
COPY favicon.ico /usr/share/nginx/html/
RUN chown -R nginx:nginx /etc/nginx/ /usr/share/nginx/html/ /var/cache/nginx
RUN chown -R nginx:nginx /etc/nginx/ /usr/share/nginx/html/ /var/cache/nginx /var/log/nginx

View File

@@ -8,9 +8,9 @@
## Overview
This project aims to provide a simple and complete user interface for your private docker registry. You can customize the interface with various options. The major option is `SINGLE_REGISTRY` which allows you to disable the dynamic selection of docker registeries (same behavior as the old **static** tag).
This project aims to provide a simple and complete user interface for your private docker registry. You can customize the interface with various options. The major option is `SINGLE_REGISTRY` which allows you to disable the dynamic selection of docker registries (same behavior as the old **static** tag).
You may need the [migration guide from 1.x to 2.x](https://github.com/Joxit/docker-registry-ui/wiki/Migrating-from-1.x-to-2.x) or [the 1.x readme](https://github.com/Joxit/docker-registry-ui/blob/8fe3adf12540d1316cb57628ebe86a392a703d90/README.md)
You may need the [migration guide from 1.x to 2.x](https://github.com/Joxit/docker-registry-ui/wiki/Migrating-from-1.x-to-2.x) or [the 1.x readme](https://github.com/Joxit/docker-registry-ui/blob/8fe3adf12540d1316cb57628ebe86a392a703d90/README.md). The project support both [docker registry v2](https://github.com/distribution/distribution/releases/tag/v2.0.0) and [docker registry v3](https://github.com/distribution/distribution/releases/tag/v3.0.0).
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.
@@ -67,12 +67,12 @@ Checkout all options in [Available options](#available-options) section.
- 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).
- Why OPTIONS (aka preflight requests) and DELETE fails with 401 status code (using Basic Auth) ?
- This is caused by a bug in docker registry, it returns 401 status requests on preflight requests, this breaks [W3C preflight-request specification](https://www.w3.org/TR/cors/#preflight-request). I suggest to have your UI on the same domain than your registry e.g. registry.example.com/ui/ **or** use `NGINX_PROXY_PASS_URL` **or** configure a nginx/apache/haproxy in front of your registry that returns 200 on each OPTIONS requests. (see [#104](https://github.com/Joxit/docker-registry-ui/issues/104), [#204](https://github.com/Joxit/docker-registry-ui/issues/204), [#207](https://github.com/Joxit/docker-registry-ui/issues/207), [#214](https://github.com/Joxit/docker-registry-ui/issues/214), [#266](https://github.com/Joxit/docker-registry-ui/issues/266)).
- Why OPTIONS (aka preflight requests) and DELETE fails with 401 status code (using Basic Auth) or why the UI says to check my `Access-Control-Allow-Origin` ?
- This is caused by a bug in docker registry, it returns 401 status requests on preflight requests, this breaks [W3C preflight-request specification](https://www.w3.org/TR/cors/#preflight-request). I contacted docker registry maintainers and this will never be fixed ([distribution/distribution#4458](https://github.com/distribution/distribution/issues/4458)). I suggest to have your UI on the same domain than your registry e.g. registry.example.com/ui/ **or** use `NGINX_PROXY_PASS_URL` **or** configure a nginx/apache/haproxy in front of your registry that returns 200 on each OPTIONS requests. (see [#104](https://github.com/Joxit/docker-registry-ui/issues/104), [#204](https://github.com/Joxit/docker-registry-ui/issues/204), [#207](https://github.com/Joxit/docker-registry-ui/issues/207), [#214](https://github.com/Joxit/docker-registry-ui/issues/214), [#266](https://github.com/Joxit/docker-registry-ui/issues/266), [#278](https://github.com/Joxit/docker-registry-ui/issues/278)).
- Can I use the docker registry ui as a standalone application (with Electron) ?
- Yes, check out the example [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/electron). (see [#129](https://github.com/Joxit/docker-registry-ui/pull/129))
- I deleted images through the UI, but they are still present on the server. How can I delete them?
- When you delete an image with the UI, only the reference is deleted and not the content. To remove dangling images, you need to run the garbage collector of the registry with the command `registry garbage-collect config.yml` or `docker exec registry registry garbage-collect config.yml`. (see [#77](https://github.com/Joxit/docker-registry-ui/issues/77) [#147](https://github.com/Joxit/docker-registry-ui/issues/147))
- When you delete an image with the UI, only the reference is deleted and not the content. To remove dangling images, you need to run the garbage collector of the registry with the command `registry garbage-collect config.yml` or `docker exec registry registry garbage-collect config.yml`. (see [#77](https://github.com/Joxit/docker-registry-ui/issues/77), [#147](https://github.com/Joxit/docker-registry-ui/issues/147))
- Why when I delete one tag, all tags with the same SHA are deleted ?
- This a docker registry API limitation, there is only one way to [delete images with tag](https://docs.docker.com/registry/spec/api/#deleting-an-image), it's by its `name` and its `manifest` (it's a sha of the content). So when you delete a tag, this will delete all tags of this image with the same SHA/manifest.
- Can I run the container with an unprivileged user ?
@@ -83,6 +83,8 @@ Checkout all options in [Available options](#available-options) section.
- You should add a CORS Policy on your bucket, check the issue [#193](https://github.com/Joxit/docker-registry-ui/issues/193).
- Why my docker registry server is returning an error `pagination number invalid` ?
- Since docker registry server 2.8.2 there is default limit of 1000 images in catalog. If you need more images update the configuration `REGISTRY_CATALOG_MAXENTRIES` with your max value and check the issue [#306](https://github.com/Joxit/docker-registry-ui/issues/306).
- I'm using `NGINX_PROXY_PASS_URL`, my registry server has been recreated and the UI cannot connect with the message `[error] 176#176: *2 connect() failed (111: Connection refused) while connecting to upstream`, what can I do?
- Nginx get the IP of all addresses only once at runtime, since your container has been recreated, its IP changed too. To prevent this kind of issue, you may use the option `NGINX_RESOLVER` and set to `127.0.0.11`.
Need more informations ? Try my [examples](https://github.com/Joxit/docker-registry-ui/tree/main/examples) or open an issue.
@@ -103,6 +105,7 @@ Some env options are available for use this interface for **only one server** (w
- `NGINX_PROXY_HEADER_*`: Update the default Nginx configuration and **set custom headers** for your backend docker registry via environment variable and file (`/etc/nginx/.env`). Only when `NGINX_PROXY_PASS_URL` is used (see [#89](https://github.com/Joxit/docker-registry-ui/pull/89)). Since 1.2.3
- `NGINX_PROXY_PASS_HEADER_*`: Update the default Nginx configuration and **forward custom headers** to your backend docker registry via environment variable and file (`/etc/nginx/.env`). Only when `NGINX_PROXY_PASS_URL` is used (see [#206](https://github.com/Joxit/docker-registry-ui/issues/206)). Since 2.1.0
- `NGINX_LISTEN_PORT`: Listen on a port other than 80, you can also change the default user and set to nginx `--user nginx` (see [#224](https://github.com/Joxit/docker-registry-ui/issues/224) and [#234](https://github.com/Joxit/docker-registry-ui/pull/234)). (default: `80` when the user is root, `8080` otherwise). Since 2.2.0
- `NGINX_RESOLVER`: Add [`resolver`](http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver) directive to the nginx configuration for dynamic dns resolving. The value when you are using a docker network is `127.0.0.11`, you can set a custom DNS server too with a valid time. This is not needed when you are using kubernetes. (see [#333](https://github.com/Joxit/docker-registry-ui/issues/333) and [#339](https://github.com/Joxit/docker-registry-ui/issues/339)). (default: ``). Since 2.5.5
- `DEFAULT_REGISTRIES`: List of comma separated registry URLs (e.g `http://registry.example.com,http://registry:5000`), available only when `SINGLE_REGISTRY=false` (see [#219](https://github.com/Joxit/docker-registry-ui/pull/219)). (default: ` `). Since 2.1.0
- `READ_ONLY_REGISTRIES`: Deactivate dialog for remove and add new registries, available only when `SINGLE_REGISTRY=false` (see [#219](https://github.com/Joxit/docker-registry-ui/pull/219)). (default: `false`). Since 2.1.0
- `SHOW_CATALOG_NB_TAGS`: Show number of tags per images on catalog page and hide images with 0 tags. This will produce + nb images requests, **not recommended on large registries** (see [#161](https://github.com/Joxit/docker-registry-ui/issues/161) and [#239](https://github.com/Joxit/docker-registry-ui/pull/239)). (default: `false`). Since 2.2.0
@@ -112,12 +115,14 @@ Some env options are available for use this interface for **only one server** (w
- `THEME_*`: See table in [Theme options](#theme-options) section (see [#283](https://github.com/Joxit/docker-registry-ui/pull/283)). Since 2.4.0
- `TAGLIST_ORDER`: Set the default order for the taglist page, could be `num-asc;alpha-asc`, `num-desc;alpha-asc`, `num-asc;alpha-desc`, `num-desc;alpha-desc`, `alpha-asc;num-asc`, `alpha-asc;num-desc`, `alpha-desc;num-asc` or `alpha-desc;num-desc` (see [#307](https://github.com/Joxit/docker-registry-ui/pull/307)). (default: `alpha-asc;num-desc`). Since 2.5.0
- `CATALOG_DEFAULT_EXPANDED`: Expand by default all repositories in catalog (see [#302](https://github.com/Joxit/docker-registry-ui/issues/302)). (default: `false`). Since 2.5.0
- `CATALOG_MIN_BRANCHES`: Set the minimum repository/namespace to expand (e.g. `joxit/docker-registry-ui` `joxit/` is the repository/namespace). Can be 0 to disable branching. (see [#319](https://github.com/Joxit/docker-registry-ui/pull/319)). (default: `1`). Since 2.5.0
- `CATALOG_MAX_BRANCHES`: Set the maximum repository/namespace to expand (e.g. `joxit/docker-registry-ui` `joxit/` is the repository/namespace). Can be 0 to disable branching. (see [#319](https://github.com/Joxit/docker-registry-ui/pull/319)). (default: `1`). Since 2.5.0
- `CATALOG_MIN_BRANCHES`: Set the minimum repository/namespace to expand (e.g. `joxit/docker-registry-ui` `joxit/` is the repository/namespace). Branching can be disabled if min and max are set to 0. (see [#319](https://github.com/Joxit/docker-registry-ui/pull/319)). (default: `1`). Since 2.5.0
- `CATALOG_MAX_BRANCHES`: Set the maximum repository/namespace to expand (e.g. `joxit/docker-registry-ui` `joxit/` is the repository/namespace). Branching can be disabled if min and max are set to 0. (see [#319](https://github.com/Joxit/docker-registry-ui/pull/319)). (default: `1`). Since 2.5.0
- `TAGLIST_PAGE_SIZE`: Set the number of tags to display in one page. (default: `100`). Since 2.5.0
- `REGISTRY_SECURED`: By default, the UI will check on every requests if your registry is secured or not (you will see `401` responses in your console). Set to `true` if your registry uses Basic Authentication and divide by two the number of call to your registry. (default `false`). Since 2.5.0
- `SHOW_TAG_HISTORY`: Whether to show the tag history feature or not. Allows to simplify the user interface by hiding it form the tag list if set to `false`. (default: `true`).
There are some examples with [docker-compose](https://docs.docker.com/compose/) and docker-registry-ui as proxy [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-proxy/) or docker-registry-ui as standalone [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-standalone/).
- `DOCKER_REGISTRY_UI_TITLE`: Set a custom title displayed in the header bar. (default: `Docker Registry UI`).
- `ENABLE_VERSION_NOTIFICATION`: Display notification when a new version of Docker Registry UI is available. This is a weekly check. (default: `true`).
### Theme options
@@ -125,16 +130,17 @@ This featureswas added to version 2.4.0. See more about this in [#283](https://g
| Environment variable | light theme value | dark theme value |
| --- | --- | --- |
| `THEME_PRIMARY_TEXT` | `#25313b` | `#8A9EBA` |
| `THEME_NEUTRAL_TEXT` | `#777777` | `#36527A` |
| `THEME_PRIMARY_TEXT` | `#25313b` | `#98a8bd` |
| `THEME_NEUTRAL_TEXT` | `#777777` | `#6d7fab` |
| `THEME_BACKGROUND` | `#ffffff` | `#22272e` |
| `THEME_HOVER_BACKGROUND` | `#eeeeee` | `#30404D` |
| `THEME_ACCENT_TEXT` | `#6680a1` | `#5684FF` |
| `THEME_HOVER_BACKGROUND` | `#eeeeee` | `#343a4b` |
| `THEME_ACCENT_TEXT` | `#5f7796` | `#5c88ff` |
| `THEME_HEADER_TEXT` | `#ffffff` | `#ffffff` |
| `THEME_HEADER_BACKGROUND` | `#25313b` | `#333A45` |
| `THEME_HEADER_ACCENT_TEXT` | `#7b9ac2` | `#7ea1ff` |
| `THEME_HEADER_BACKGROUND` | `#25313b` | `#333a45` |
| `THEME_FOOTER_TEXT` | `#ffffff` | `#ffffff` |
| `THEME_FOOTER_NEUTRAL_TEXT` | `#999999` | `#999999` |
| `THEME_FOOTER_BACKGROUND` | `#555555` | `#555555` |
| `THEME_FOOTER_NEUTRAL_TEXT` | `#adbacd` | `#98afcf` |
| `THEME_FOOTER_BACKGROUND` | `#344251` | `#344251` |
## Recommended Docker Registry Usage
@@ -167,9 +173,9 @@ services:
image: registry:2.8.2
restart: always
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Origin: '[http://registry.example.com]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '[http://registry-ui.example.com]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]'
REGISTRY_HTTP_HEADERS_Access-Control-Credentials: '[true]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '[true]'
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]'
REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]'
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
@@ -180,18 +186,26 @@ services:
## Using CORS
Your server should be configured to accept CORS.
:warning: Before posting issues about CORS, please read the and all created issues.
If your docker registry does not need credentials, you will need to send this HEADER:
:warning: If you **are using credentials** and your registry is on a different host than your UI, please read the [FAQ about OPTIONS](https://github.com/Joxit/docker-registry-ui#:~:text=Why%20OPTIONS%20(aka%20preflight%20requests)), all the linked issues and [distribution/distribution#4458](https://github.com/distribution/distribution/issues/4458) first. The best way for the UI to work is using `NGINX_PROXY_PASS_URL` or configure your own proxy (nginx, haproxy...) that will be on top of your **docker registry** (and not the UI!) to override OPTIONS requests.
If your docker registry **does not need credentials**, you will need to send this HEADER:
```yml
http:
headers:
Access-Control-Allow-Origin: ['*']
Access-Control-Allow-Headers: ['Accept', 'Cache-Control']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS'] # Optional
```
If your docker registry need credentials, you will need to send these HEADERS (you must add the protocol `http`/`https` and the port when not default `80`/`443`):
```yml
http:
headers:
Access-Control-Allow-Origin: ['http://registry.example.com']
Access-Control-Allow-Origin: ['http://registry-ui.example.com']
Access-Control-Allow-Credentials: [true]
Access-Control-Allow-Headers: ['Authorization', 'Accept', 'Cache-Control']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS'] # Optional
@@ -199,8 +213,6 @@ http:
An alternative for CORS issues is a plugin on your browser, more info [here](https://github.com/Joxit/docker-registry-ui/issues/25#issuecomment-621104846) (thank you [xmontero](https://github.com/xmontero)).
:warning: If you are using credential and still having issues, please read the the line about preflight requests and the bug in docker registry server in the [FAQ](#faq) before posting any issues.
## Using delete
For deleting images, you need to activate the delete feature in the UI with `DELETE_IMAGES=true` and in your registry:

View File

@@ -27,4 +27,4 @@ COPY bin/90-docker-registry-ui.sh /docker-entrypoint.d/90-docker-registry-ui.sh
COPY dist/ /usr/share/nginx/html/
COPY favicon.ico /usr/share/nginx/html/
RUN chown -R nginx:nginx /etc/nginx/ /usr/share/nginx/html/ /var/cache/nginx
RUN chown -R nginx:nginx /etc/nginx/ /usr/share/nginx/html/ /var/cache/nginx /var/log/nginx

View File

@@ -27,4 +27,4 @@ COPY bin/90-docker-registry-ui.sh /docker-entrypoint.d/90-docker-registry-ui.sh
COPY dist/ /usr/share/nginx/html/
COPY favicon.ico /usr/share/nginx/html/
RUN chown -R nginx:nginx /etc/nginx/ /usr/share/nginx/html/ /var/cache/nginx
RUN chown -R nginx:nginx /etc/nginx/ /usr/share/nginx/html/ /var/cache/nginx /var/log/nginx

View File

@@ -1,11 +1,13 @@
#!/bin/sh
sed -i "s~\${DOCKER_REGISTRY_UI_TITLE}~${DOCKER_REGISTRY_UI_TITLE}~" index.html
sed -i "s~\${REGISTRY_URL}~${REGISTRY_URL}~" index.html
sed -i "s~\${REGISTRY_TITLE}~${REGISTRY_TITLE}~" index.html
sed -i "s~\${PULL_URL}~${PULL_URL}~" index.html
sed -i "s~\${SINGLE_REGISTRY}~${SINGLE_REGISTRY}~" index.html
sed -i "s~\${CATALOG_ELEMENTS_LIMIT}~${CATALOG_ELEMENTS_LIMIT}~" index.html
sed -i "s~\${SHOW_CONTENT_DIGEST}~${SHOW_CONTENT_DIGEST}~" index.html
sed -i "s~\${SHOW_TAG_HISTORY}~${SHOW_TAG_HISTORY}~" index.html
sed -i "s~\${DEFAULT_REGISTRIES}~${DEFAULT_REGISTRIES}~" index.html
sed -i "s~\${READ_ONLY_REGISTRIES}~${READ_ONLY_REGISTRIES}~" index.html
sed -i "s~\${SHOW_CATALOG_NB_TAGS}~${SHOW_CATALOG_NB_TAGS}~" index.html
@@ -17,6 +19,7 @@ sed -i "s~\${CATALOG_MIN_BRANCHES}~${CATALOG_MIN_BRANCHES}~" index.html
sed -i "s~\${CATALOG_MAX_BRANCHES}~${CATALOG_MAX_BRANCHES}~" index.html
sed -i "s~\${TAGLIST_PAGE_SIZE}~${TAGLIST_PAGE_SIZE}~" index.html
sed -i "s~\${REGISTRY_SECURED}~${REGISTRY_SECURED}~" index.html
sed -i "s~\${ENABLE_VERSION_NOTIFICATION}~${ENABLE_VERSION_NOTIFICATION}~" index.html
grep -o 'THEME[A-Z_]*' index.html | while read e; do
sed -i "s~\${$e}~$(printenv $e)~" index.html
@@ -65,7 +68,13 @@ if [ -n "${NGINX_PROXY_PASS_URL}" ] ; then
sed -i "s,\${NGINX_PROXY_PASS_URL},${NGINX_PROXY_PASS_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^\${NGINX_PROXY_PASS_HEADERS}^$(get_nginx_proxy_pass_headers)^" /etc/nginx/conf.d/default.conf
sed -i "s,#!,," /etc/nginx/conf.d/default.conf
sed -i "s,#! , ," /etc/nginx/conf.d/default.conf # The space is important here, to not interfer with #!r
if [ -n "${NGINX_RESOLVER}" ]; then
sed -i "s,\${NGINX_RESOLVER},${NGINX_RESOLVER}," /etc/nginx/conf.d/default.conf
sed -i "s,#r,," /etc/nginx/conf.d/default.conf
else
sed -i "s,#!r, ," /etc/nginx/conf.d/default.conf # The space is for cosmetic here
fi
fi
if [ "$(whoami)" != "root" ]; then
@@ -73,7 +82,7 @@ if [ "$(whoami)" != "root" ]; then
NGINX_LISTEN_PORT="8080"
fi
sed -i "/user nginx;/d" /etc/nginx/nginx.conf
sed -i "s,/var/run/nginx.pid,/tmp/nginx.pid," /etc/nginx/nginx.conf
sed -i "s,/run/nginx.pid,/tmp/nginx.pid," /etc/nginx/nginx.conf
fi
if [ "$NGINX_LISTEN_PORT" != "80" ]; then

View File

@@ -27,4 +27,4 @@ COPY bin/90-docker-registry-ui.sh /docker-entrypoint.d/90-docker-registry-ui.sh
COPY dist/ /usr/share/nginx/html/
COPY favicon.ico /usr/share/nginx/html/
RUN chown -R nginx:nginx /etc/nginx/ /usr/share/nginx/html/ /var/cache/nginx
RUN chown -R nginx:nginx /etc/nginx/ /usr/share/nginx/html/ /var/cache/nginx /var/log/nginx

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
dist/fonts/material-symbols-rounded.ttf vendored Normal file

Binary file not shown.

BIN
dist/fonts/material-symbols-rounded.woff vendored Normal file

Binary file not shown.

Binary file not shown.

6
dist/index.html vendored
View File

@@ -14,10 +14,12 @@
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 href="docker-registry-ui.css" 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><docker-registry-ui
docker-registry-ui-title="${DOCKER_REGISTRY_UI_TITLE}"
registry-url="${REGISTRY_URL}"
name="${REGISTRY_TITLE}"
pull-url="${PULL_URL}"
show-content-digest="${SHOW_CONTENT_DIGEST}"
show-tag-history="${SHOW_TAG_HISTORY}"
is-image-remove-activated="${DELETE_IMAGES}"
catalog-elements-limit="${CATALOG_ELEMENTS_LIMIT}"
single-registry="${SINGLE_REGISTRY}"
@@ -37,10 +39,12 @@
theme-background="${THEME_BACKGROUND}"
theme-hover-background="${THEME_HOVER_BACKGROUND}"
theme-accent-text="${THEME_ACCENT_TEXT}"
theme-header-accent-text="${THEME_HEADER_ACCENT_TEXT}"
theme-header-text="${THEME_HEADER_TEXT}"
theme-header-background="${THEME_HEADER_BACKGROUND}"
theme-footer-text="${THEME_FOOTER_TEXT}"
theme-footer-neutra-text="${THEME_FOOTER_NEUTRAL_TEXT}"
theme-footer-neutral-text="${THEME_FOOTER_NEUTRAL_TEXT}"
theme-footer-background="${THEME_FOOTER_BACKGROUND}"
tags-per-page="${TAGLIST_PAGE_SIZE}"
enable-version-notification="${ENABLE_VERSION_NOTIFICATION}"
></docker-registry-ui><script src="docker-registry-ui.js"></script></body></html>

1
dist/version.json vendored Normal file
View File

@@ -0,0 +1 @@
{"version":"2.6.0","latest":"2.6.0"}

View File

@@ -2,9 +2,12 @@
This example will override the original nginx conf with read only access to the registry. You will need to rewrite all the project configuration (replaces `proxy_pass` with your own value, in this example `http://registry:5000` is fine).
There are two htpasswd files. `read-write.htpasswd` a read and write access to the registry and `read-only.htpasswd` for a read only access.
There are two htpasswd files:
All users in `read-only.htpasswd` should be in `read-write.htpasswd`.
- `write.htpasswd` for write access
- `read.htpasswd` for read access
All users in `write.htpasswd` should also be in `read.htpasswd` so that they can read and write.
Read only user: login: `read` password: `registry`.
Read and write user: login: `write` password: `registry`.

View File

@@ -17,11 +17,11 @@ services:
- SINGLE_REGISTRY=true
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./read-write.htpasswd:/etc/nginx/auth/read-write.htpasswd:ro
- ./read-only.htpasswd:/etc/nginx/auth/read-only.htpasswd
- ./write.htpasswd:/etc/nginx/auth/write.htpasswd:ro
- ./read.htpasswd:/etc/nginx/auth/read.htpasswd:ro
depends_on:
- registry
networks:
- registry-ui-net
networks:
registry-ui-net:
registry-ui-net:

View File

@@ -28,10 +28,10 @@ server {
}
# To add basic authentication to v2 use auth_basic setting.
auth_basic "Registry realm";
auth_basic_user_file /etc/nginx/auth/read-write.htpasswd;
# For requests that *aren't* a PUT, POST, or DELETE
limit_except PUT POST DELETE {
auth_basic_user_file /etc/nginx/auth/read-only.htpasswd;
auth_basic_user_file /etc/nginx/auth/read.htpasswd;
# For requests that *aren't* a GET, HEAD or OPTIONS use the write file instead
limit_except GET HEAD OPTIONS {
auth_basic_user_file /etc/nginx/auth/write.htpasswd;
}
proxy_pass http://registry:5000;

View File

@@ -1,12 +1,15 @@
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;
#r resolver ${NGINX_RESOLVER}; # 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
# disable any limits to avoid HTTP 413 for large image uploads and 400 on large headers (eg: cookie)
client_max_body_size 0;
client_body_buffer_size 32k;
client_header_buffer_size 8k;
large_client_header_buffers 8 64k;
# required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
chunked_transfer_encoding on;
@@ -31,10 +34,12 @@ server {
#! proxy_http_version 1.1;
#! ${NGINX_PROXY_HEADERS}
#! ${NGINX_PROXY_PASS_HEADERS}
#! proxy_pass ${NGINX_PROXY_PASS_URL};
#r set $registry_server "${NGINX_PROXY_PASS_URL}";
#r proxy_pass $registry_server;
#!r proxy_pass ${NGINX_PROXY_PASS_URL};
#! }
#error_page 404 /404.html;
# error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
@@ -42,11 +47,4 @@ server {
location = /50x.html {
root /usr/share/nginx/html;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

View File

@@ -1,6 +1,6 @@
{
"name": "docker-registry-ui",
"version": "2.5.2",
"version": "2.6.0",
"type": "module",
"scripts": {
"format": "npm run format-html && npm run format-js && npm run format-riot",
@@ -22,26 +22,26 @@
"devDependencies": {
"@babel/core": "^7.20.7",
"@babel/preset-env": "^7.20.2",
"@riotjs/compiler": "^6.4.2",
"@riotjs/compiler": "^9.4.1",
"@riotjs/observable": "^4.1.1",
"@riotjs/route": "^8.0.2",
"@riotjs/route": "^9.2.1",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-html": "^1.0.1",
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-html": "^2.0.0",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-terser": "^0.2.1",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4",
"core-js": "^3.27.1",
"mocha": "^10.2.0",
"node-sass": "^8.0.0",
"prettier": "^2.8.1",
"riot": "^7.1.0",
"mocha": "^11.2.0",
"prettier": "^3.4.2",
"riot": "^9.4.4",
"riot-mui": "github:joxit/riot-5-mui#a477acc",
"rollup": "^3.9.0",
"rollup": "^4.30.1",
"rollup-plugin-app-utils": "^1.0.6",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-riot": "^6.0.0",
"rollup-plugin-riot": "^9.0.2",
"rollup-plugin-scss": "^4.0.0",
"rollup-plugin-serve": "^2.0.2"
"rollup-plugin-serve": "^3.0.0",
"sass": "^1.86.2"
}
}

View File

@@ -43,6 +43,7 @@ const plugins = [
copy({
targets: [
{ src: 'src/fonts', dest: `${output}` },
{ src: '.version.json', dest: `${output}`, rename: 'version.json' },
{ src: 'src/images/*', dest: `${output}/images`, transform: copyTransform },
],
}),

View File

@@ -111,7 +111,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
onAuthentication: props.onAuthentication,
withCredentials: props.isRegistrySecured,
});
oReq.addEventListener('load', function () {
oReq.addEventListener('loadend', function () {
if (this.status === 200) {
const nbTags = (JSON.parse(this.responseText).tags || []).length;
self.update({ nbTags });

View File

@@ -16,7 +16,18 @@
-->
<confirm-delete-image>
<material-popup opened="{ props.opened }" onClick="{ props.onClick }">
<div class="material-popup-title">These images will be deleted</div>
<div class="material-popup-title">
These images will be deleted
<material-button
color="inherit"
text-color="var(--accent-text)"
target="_blank"
waves-color="var(--hover-background)"
href="https://joxit.dev/docker-registry-ui/#:~:text=Why%20when%20I%20delete%20one%20tag,%20all%20tags%20with%20the%20same%20SHA%20are%20deleted%20"
icon
><i class="material-icons">help</i>
</material-button>
</div>
<div class="material-popup-content">
<ul>
<li each="{ image in displayImagesToDelete(props.toDelete, props.tags) }">{ image.name }:{ image.tag }</li>

View File

@@ -18,8 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<header>
<material-navbar>
<span class="logo">
<span>Docker Registry UI</span>
<version-notification version="{ latest }" on-notify="{ notifySnackbar }"></version-notification>
<span><a href="{ router.home() }">{ props.dockerRegistryUiTitle.trim() || "Docker Registry UI" }</a></span>
<version-notification
if="{ falsy(props.enableVersionNotification) }"
version="{ latest }"
on-notify="{ notifySnackbar }"
></version-notification>
</span>
<div class="menu">
<search-bar on-search="{ onSearch }"></search-bar>
@@ -49,7 +53,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
message="{ error.message }"
url="{ state.pageError.url }"
></error-page>
<router base="#!">
<router>
<route path="{baseRoute}">
<catalog
registry-url="{ state.registryUrl }"
@@ -72,6 +76,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
pull-url="{ state.pullUrl }"
image="{ router.getTagListImage() }"
show-content-digest="{ truthy(props.showContentDigest) }"
show-tag-history="{ falsy(props.showTagHistory) }"
is-image-remove-activated="{ truthy(props.isImageRemoveActivated) }"
on-notify="{ notifySnackbar }"
filter-results="{ state.filter }"
@@ -146,7 +151,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import SearchBar from './search-bar.riot';
import ErrorPage from './error-page.riot';
import VersionNotification from './version-notification.riot';
import { stripHttps, getRegistryServers, setRegistryServers, truthy, stringToArray } from '../scripts/utils';
import { stripHttps, getRegistryServers, setRegistryServers, truthy, falsy, stringToArray } from '../scripts/utils';
import router from '../scripts/router';
import { loadTheme } from '../scripts/theme';
@@ -262,6 +267,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
version,
latest,
truthy,
falsy,
stringToArray,
};
</script>
@@ -295,6 +301,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
flex-direction: row;
}
material-navbar .nav-wrapper .logo a {
text-decoration: none;
}
material-navbar .nav-wrapper .logo a:hover {
text-decoration: underline;
}
material-footer {
color: var(--footer-neutral-text);
background-color: var(--footer-background);

View File

@@ -21,7 +21,7 @@
<p>This request <span>may</span> has been blocked; the content must be served over HTTPS.</p>
<p>
You may unset the option `<span>REGISTRY_URL</span>` and set the registry server container URL in
`<span>NGINX_PROXY_PASS_URL</span>`. It's usually the name of your container, and it should be on the shame
`<span>NGINX_PROXY_PASS_URL</span>`. It's usually the name of your container, and it should be on the same
network as the UI.
</p>
<p>You can check the issue <a href="https://github.com/Joxit/docker-registry-ui/issues/277">#277</a>.</p>
@@ -63,7 +63,7 @@
</template>
<template if="{ props.code === 'CATALOG_BRANCHING_CONFIGURATION' }">
<p>Wrong configuration for the branching feature: { props.message }</p>
<p>Configuration environment variables are : CATALOG_MIN_BRANCH and CATALOG_MAX_BRANCH</p>
<p>Configuration environment variables are : CATALOG_MIN_BRANCHES and CATALOG_MAX_BRANCHES</p>
</template>
</div>
<script>

View File

@@ -2,8 +2,8 @@
<material-input
label="Search in page"
text-color="var(--header-text)"
label-color="var(--neutral-text)"
color="var(--accent-text)"
label-color="var(--header-accent-text)"
color="var(--header-accent-text)"
></material-input>
<script>
import { router } from '@riotjs/route';

View File

@@ -253,8 +253,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
'author',
'id',
'ExposedPorts',
'name',
'appVersion',
'kubeVersion',
'keywords',
'home',
'sources'
].reduce(function (acc, e) {
const value = blobs[e] || blobs.config[e];
const value = blobs[e] || (blobs.config && blobs.config[e]);
if (value && e === 'architecture' && blobs.variant) {
acc[e] = value + blobs.variant;
} else if (value) {
@@ -287,5 +293,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
flex-direction: row;
align-items: center;
}
h2 .material-icons {
margin-left: .25em;
}
</style>
</tag-history>

View File

@@ -50,6 +50,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
asc="{state.asc}"
page="{ state.page }"
show-content-digest="{props.showContentDigest}"
show-tag-history="{props.showTagHistory}"
is-image-remove-activated="{props.isImageRemoveActivated}"
onReverseOrder="{ onReverseOrder }"
registry-url="{ props.registryUrl }"
@@ -129,7 +130,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
.sort(self.tagComparator);
window.requestAnimationFrame(self.onResize);
self.update({
page: Math.min(state.page, getNumPages(tags)),
page: Math.min(state.page, getNumPages(tags, props.tagsPerPage)),
tags,
});
} else if (this.status === 404) {
@@ -153,7 +154,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
},
onPageUpdate(idx) {
const labels = getPageLabels(this.state.page, getNumPages(this.state.tags));
const labels = getPageLabels(this.state.page, getNumPages(this.state.tags, this.props.tagsPerPage));
const page = labels[idx].page;
this.update({
page: page,

View File

@@ -52,7 +52,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
Tag
</th>
<th class="architectures">Arch</th>
<th class="show-tag-history">History</th>
<th class="show-tag-history" if="{ props.showTagHistory }">History</th>
<th
class="remove-tag { state.toDelete.size > 0 && !state.singleDeleteAction ? 'delete' : '' }"
if="{ props.isImageRemoveActivated }"
@@ -109,7 +109,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<td class="architectures">
<architectures image="{ image }"></architectures>
</td>
<td class="show-tag-history">
<td class="show-tag-history" if="{ props.showTagHistory }">
<tag-history-button image="{ image }"></tag-history-button>
</td>
<td if="{ props.isImageRemoveActivated }" class="remove-tag">

View File

@@ -43,7 +43,7 @@
if (latest && latest.tag_name) {
this.update({ tag_name: latest.tag_name, latest });
}
if (!latest || isNaN(expires) || new Date().getTime() > expires) {
if (isNaN(expires) || new Date().getTime() > expires) {
this.checkForUpdates(props, state);
}
},
@@ -64,16 +64,22 @@
const self = this;
oReq.addEventListener('load', function () {
localStorage.setItem(EXPIRES, new Date().getTime() + ONE_DAY * 7);
if (this.status === 200) {
const latest = parseJSON(this.responseText);
if (latest && self.tag_name !== latest.tag_name && !isNewestVersion(props.version, latest.tag_name)) {
props.onNotify('A new version of Docker Registry UI is available!');
}
localStorage.setItem(LATEST, this.responseText);
localStorage.setItem(EXPIRES, new Date().getTime() + ONE_DAY);
self.update({ tag_name: latest.tag_name, latest });
} else {
props.onNotify('Cannot check for new updates. See the browser console.');
} else if (this.status !== 404) {
// Should not notify if the project is not found.
props.onNotify('Cannot check for new updates. Will try again in 24 hours. See the browser console.');
console.error(
`Cannot check for new Docker Registry UI updates. This is most likely a GitHub issue. You don't need to worry about it.`
);
console.error(`Got status code ${this.status} from Github API with response ${this.responseText}`);
}
});

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -35,10 +35,12 @@
<body>
<!-- build:keep production -->
<docker-registry-ui
docker-registry-ui-title="${DOCKER_REGISTRY_UI_TITLE}"
registry-url="${REGISTRY_URL}"
name="${REGISTRY_TITLE}"
pull-url="${PULL_URL}"
show-content-digest="${SHOW_CONTENT_DIGEST}"
show-tag-history="${SHOW_TAG_HISTORY}"
is-image-remove-activated="${DELETE_IMAGES}"
catalog-elements-limit="${CATALOG_ELEMENTS_LIMIT}"
single-registry="${SINGLE_REGISTRY}"
@@ -58,21 +60,25 @@
theme-background="${THEME_BACKGROUND}"
theme-hover-background="${THEME_HOVER_BACKGROUND}"
theme-accent-text="${THEME_ACCENT_TEXT}"
theme-header-accent-text="${THEME_HEADER_ACCENT_TEXT}"
theme-header-text="${THEME_HEADER_TEXT}"
theme-header-background="${THEME_HEADER_BACKGROUND}"
theme-footer-text="${THEME_FOOTER_TEXT}"
theme-footer-neutra-text="${THEME_FOOTER_NEUTRAL_TEXT}"
theme-footer-neutral-text="${THEME_FOOTER_NEUTRAL_TEXT}"
theme-footer-background="${THEME_FOOTER_BACKGROUND}"
tags-per-page="${TAGLIST_PAGE_SIZE}"
enable-version-notification="${ENABLE_VERSION_NOTIFICATION}"
>
</docker-registry-ui>
<!-- endbuild -->
<!-- build:keep developement -->
<docker-registry-ui
docker-registry-ui-title=""
registry-url=""
name="Developement Registry"
name="Development Registry"
pull-url=""
show-content-digest="true"
show-tag-history="true"
is-image-remove-activated="true"
catalog-elements-limit="1000"
single-registry="false"
@@ -90,12 +96,14 @@
theme-background=""
theme-hover-background=""
theme-accent-text=""
theme-header-accent-text=""
theme-header-text=""
theme-header-background=""
theme-footer-text=""
theme-footer-neutra-text=""
theme-footer-neutral-text=""
theme-footer-background=""
tags-per-page=""
enable-version-notification="true"
>
</docker-registry-ui>
<!-- endbuild -->

View File

@@ -1,20 +1,18 @@
@font-face {
font-family: 'Material Icons';
font-family: 'Material Symbols Rounded';
font-style: normal;
font-weight: 400;
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');
src: local('Material Symbols Rounded'),
url(fonts/material-symbols-rounded.woff2) format('woff2'),
url(fonts/material-symbols-rounded.woff) format('woff'),
url(fonts/material-symbols-rounded.ttf) format('truetype');
}
material-button .content i.material-icons,
material-button[rounded=true] .content i.material-icons,
i.material-icons {
font-family: 'Material Icons';
font-family: 'Material Symbols Rounded';
font-weight: normal;
font-style: normal;
font-size: 24px; /* Preferred icon size */
@@ -38,6 +36,12 @@ i.material-icons {
/* Support for IE. */
font-feature-settings: 'liga';
user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
}
material-button .content i.material-icons,

View File

@@ -17,16 +17,20 @@ export const getFromCache = (method, url) => {
return;
}
try {
return sessionStorage.getItem(sha256);
return {
responseText: sessionStorage.getItem(`${sha256}/responseText`),
dockerContentdigest: sessionStorage.getItem(`${sha256}/dockerContentdigest`),
};
} catch (e) {}
};
export const setCache = (method, url, responseText) => {
export const setCache = (method, url, { responseText, dockerContentdigest }) => {
const sha256 = getSha256(method, url);
if (!sha256) {
return;
}
try {
sessionStorage.setItem(sha256, responseText);
sessionStorage.setItem(`${sha256}/responseText`, responseText);
sessionStorage.setItem(`${sha256}/dockerContentdigest`, dockerContentdigest);
} catch (e) {}
};

View File

@@ -113,6 +113,7 @@ export class DockerImage {
}
self.ociImage = response.mediaType === 'application/vnd.oci.image.index.v1+json';
self.layers = response.layers || response.manifests;
self.annotations = response.annotations;
self.size = self.layers.reduce(function (acc, e) {
return acc + e.size;
}, 0);
@@ -160,8 +161,9 @@ export class DockerImage {
oReq.addEventListener('loadend', function () {
if (this.status === 200 || this.status === 202) {
const response = JSON.parse(this.responseText);
self.creationDate = new Date(response.created);
self.creationDate = new Date(response.created || self.annotations?.['org.opencontainers.image.created']);
self.blobs = response;
self.blobs.history = self.blobs.history || [];
self.blobs.history
.filter(function (e) {
return !e.empty_layer;

View File

@@ -29,19 +29,23 @@ export class Http {
}
getContentDigest(cb) {
if (this.oReq.hasHeader('Docker-Content-Digest')) {
if (this.cache?.dockerContentdigest) {
cb(this.cache.dockerContentdigest);
} else 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((byte) => byte.toString(16).padStart(2, '0'))
.join('')
);
});
crypto.subtle
.digest('SHA-256', new TextEncoder().encode(this.oReq.responseText || this.cache?.responseText))
.then(function (buffer) {
cb(
'sha256:' +
Array.from(new Uint8Array(buffer))
.map((byte) => byte.toString(16).padStart(2, '0'))
.join('')
);
});
} else {
// IE and old Edge
// simply do not call the callback and skip the setup downstream
@@ -54,9 +58,9 @@ export class Http {
switch (e) {
case 'loadend': {
self.oReq.addEventListener('loadend', function () {
if (this.status === 401 && !this.withCredentials) {
const tokenAuth =
this.hasHeader('www-authenticate') && parseAuthenticateHeader(this.getResponseHeader('www-authenticate'));
const tokenAuth =
this.hasHeader('www-authenticate') && parseAuthenticateHeader(this.getResponseHeader('www-authenticate'));
if (this.status === 401 && (!this.withCredentials || tokenAuth)) {
self.onAuthentication(tokenAuth, (bearer) => {
const req = new XMLHttpRequest();
req._url = self._url;
@@ -80,7 +84,11 @@ export class Http {
req.send();
});
} else {
this.status === 200 && setCache(self._method, self._url, this.responseText);
this.status === 200 &&
setCache(self._method, self._url, {
responseText: this.responseText,
dockerContentdigest: this.getResponseHeader('Docker-Content-Digest'),
});
f.bind(this)();
}
});
@@ -119,9 +127,10 @@ export class Http {
}
send() {
const responseText = getFromCache(this._method, this._url);
if (responseText) {
return this._events['loadend'].bind({ status: 200, responseText })();
const cache = getFromCache(this._method, this._url);
if (cache && cache.responseText) {
this.cache = cache;
return this._events['loadend'].bind({ status: 200, responseText: cache.responseText })();
}
this.oReq.send();
}

View File

@@ -1,26 +1,28 @@
const LIGHT_THEME = {
'primary-text': '#25313b',
'neutral-text': '#777',
'background': '#fff',
'hover-background': '#eee',
'accent-text': '#6680a1',
'header-text': '#fff',
'neutral-text': '#777777',
'background': '#ffffff',
'hover-background': '#eeeeee',
'accent-text': '#5f7796',
'header-text': '#ffffff',
'header-accent-text': '#7b9ac2',
'header-background': '#25313b',
'footer-text': '#fff',
'footer-neutral-text': '#999',
'footer-background': '#555',
'footer-text': '#ffffff',
'footer-neutral-text': '#adbacd',
'footer-background': '#344251',
};
const DARK_THEME = {
'primary-text': '#8A9EBA',
'neutral-text': '#36527A',
'primary-text': '#98a8bd',
'neutral-text': '#6d7fab',
'background': '#22272e',
'hover-background': '#30404D',
'accent-text': '#5684FF',
'header-text': '#fff',
'header-background': '#333A45',
'footer-text': '#fff',
'footer-neutral-text': '#999',
'footer-background': '#555',
'hover-background': '#343a4b',
'accent-text': '#5c88ff',
'header-text': '#ffffff',
'header-accent-text': '#7ea1ff',
'header-background': '#333a45',
'footer-text': '#ffffff',
'footer-neutral-text': '#98afcf',
'footer-background': '#344251',
};
const LOCAL_STORAGE_THEME = 'registryUiTheme';

View File

@@ -82,6 +82,17 @@ export function getHistoryIcon(attribute) {
return 'router';
case 'comment':
return 'chat';
case 'home':
return 'home';
case 'sources':
return 'link';
case 'keywords':
return 'receipt';
case 'name':
return 'abc';
case 'kubeVersion':
case 'appVersion':
return '123';
default:
if (attribute.startsWith('custom-label-')) {
return 'label';
@@ -217,6 +228,17 @@ export function truthy(value) {
return value === true || value === 'true';
}
/**
* only is false if explicitly set to boolean false or string 'false'.
* defaults to true in any other case, e.g. if empty.
*
* @param {string|boolean} value the input value to check
* @returns {boolean} false if explicity set, true otherwise
*/
export function falsy(value) {
return value !== false && value !== 'false';
}
export function stringToArray(value) {
return value && typeof value === 'string' ? value.split(',') : [];
}

View File

@@ -14,22 +14,22 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@import 'riot-mui/src/material-elements/material-navbar/material-navbar.scss';
@import 'riot-mui/src/material-elements/material-footer/material-footer.scss';
@import 'riot-mui/src/material-elements/material-card/material-card.scss';
@import 'riot-mui/src/material-elements/material-spinner/material-spinner.scss';
@import 'riot-mui/src/material-elements/material-button/material-button.scss';
@import 'riot-mui/src/material-elements/material-waves/material-waves.scss';
@import 'riot-mui/src/material-elements/material-checkbox/material-checkbox.scss';
@import 'riot-mui/src/material-elements/material-tabs/material-tabs.scss';
@import 'riot-mui/src/material-elements/material-snackbar/material-snackbar.scss';
@import 'riot-mui/src/material-elements/material-dropdown/material-dropdown.scss';
@import 'riot-mui/src/material-elements/material-popup/material-popup.scss';
@import 'riot-mui/src/material-elements/material-input/material-input.scss';
@import 'riot-mui/src/material-elements/material-switch/material-switch.scss';
@use 'riot-mui/src/material-elements/material-navbar/material-navbar.scss';
@use 'riot-mui/src/material-elements/material-footer/material-footer.scss';
@use 'riot-mui/src/material-elements/material-card/material-card.scss';
@use 'riot-mui/src/material-elements/material-spinner/material-spinner.scss';
@use 'riot-mui/src/material-elements/material-button/material-button.scss';
@use 'riot-mui/src/material-elements/material-waves/material-waves.scss';
@use 'riot-mui/src/material-elements/material-checkbox/material-checkbox.scss';
@use 'riot-mui/src/material-elements/material-tabs/material-tabs.scss';
@use 'riot-mui/src/material-elements/material-snackbar/material-snackbar.scss';
@use 'riot-mui/src/material-elements/material-dropdown/material-dropdown.scss';
@use 'riot-mui/src/material-elements/material-popup/material-popup.scss';
@use 'riot-mui/src/material-elements/material-input/material-input.scss';
@use 'riot-mui/src/material-elements/material-switch/material-switch.scss';
@import './roboto.scss';
@import './material-icons.scss';
@use './roboto.scss';
@use './material-symbols.scss';
html > body {
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif !important;
@@ -295,7 +295,7 @@ material-card table th.material-card-th-sorted-descending:hover:before {
material-card table th.material-card-th-sorted-ascending:before,
material-card table th.material-card-th-sorted-descending:before {
font-family: 'Material Icons';
font-family: 'Material Symbols Rounded';
font-weight: 400;
font-style: normal;
line-height: 1;