Compare commits
16 Commits
Author | SHA1 | Date |
---|---|---|
silverwind | a36e3aac57 | |
Lukas Engelter | 7025df687c | |
Lukas Engelter | cfbc6e76a8 | |
toinux | dc9bdcbedd | |
Joxit | 6c3c27e215 | |
Jones Magloire | 6318ccfdf5 | |
Christian | 686b1709b2 | |
Joxit | e79a20a5e5 | |
Joxit | 7991442fce | |
Joxit | de6d09c98c | |
Joxit | 1f2913248e | |
Joxit | 3414d7b517 | |
AJWavio | cd99f6e231 | |
Joxit | f015187b14 | |
Joxit | 07713f1425 | |
Joxit | f560025e70 |
|
@ -42,19 +42,17 @@ 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 -->
|
||||
- OS: [e.g. Debian, Windows, Mac OS, Android 9...]
|
||||
- Browser:
|
||||
- Name: [e.g. Chrome, Firefox...]
|
||||
- Version: [e.g. 22]
|
||||
- Name: [e.g. Chrome, Firefox...]
|
||||
- Version: [e.g. 114.0.5735.198, 102.11.0...]
|
||||
- Docker registry UI:
|
||||
- Version: [e.g. 1.4.0]
|
||||
- Version: [e.g. 2.5.0, 2.6.0-84b31f2cfb...]
|
||||
- Server: [docker or dist]
|
||||
<!-- Only for Docker and for where the UI is hosted -->
|
||||
- Docker version: [e.g. 19.03]
|
||||
- Docker registry ui tag: [latest, static, master, 1.4-static...]
|
||||
- Docker version: [e.g. 24.0.4...]
|
||||
- Docker registry ui tag: [latest, main, 2.5.0, 2...]
|
||||
- OS/Arch: [e.g. linux/amd64]
|
||||
- Tools: [e.g. docker-compose, kubernets..]
|
||||
- Tools: [e.g. docker-compose, kubernetes, electron..]
|
||||
|
||||
## Additional context
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# How to build Docker Registry UI
|
||||
|
||||
This file contains tips to help you take (and understand) your first steps in Docker Registry UI development.
|
||||
|
||||
## Clone and install the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Joxit/docker-registry-ui.git
|
||||
cd docker-registry-ui
|
||||
npm install
|
||||
```
|
||||
|
||||
## Run the local server
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
Open your browser <http://localhost:8000> you can configure your options by updating the `src/index.html` file.
|
24
README.md
24
README.md
|
@ -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
|
||||
|
@ -116,7 +119,7 @@ Some env options are available for use this interface for **only one server** (w
|
|||
- `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
|
||||
- `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/).
|
||||
|
||||
### Theme options
|
||||
|
@ -125,16 +128,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 +171,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.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'
|
||||
|
|
|
@ -6,6 +6,7 @@ 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
|
||||
|
@ -65,7 +66,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
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
- ./read-write.htpasswd:/etc/nginx/auth/write.htpasswd:ro
|
||||
- ./read-only.htpasswd:/etc/nginx/auth/read.htpasswd:ro
|
||||
depends_on:
|
||||
- registry
|
||||
networks:
|
||||
- registry-ui-net
|
||||
networks:
|
||||
registry-ui-net:
|
||||
registry-ui-net:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
#}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "docker-registry-ui",
|
||||
"version": "2.5.1",
|
||||
"version": "2.5.7",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"format": "npm run format-html && npm run format-js && npm run format-riot",
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -72,6 +72,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 +147,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,10 +263,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
version,
|
||||
latest,
|
||||
truthy,
|
||||
falsy,
|
||||
stringToArray,
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
:host main {
|
||||
flex-grow: 1;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
material-navbar {
|
||||
height: 64px;
|
||||
color: var(--header-text);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -287,5 +287,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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
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}`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
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,10 +59,11 @@
|
|||
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}"
|
||||
>
|
||||
|
@ -70,9 +72,10 @@
|
|||
<!-- build:keep developement -->
|
||||
<docker-registry-ui
|
||||
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,10 +93,11 @@
|
|||
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=""
|
||||
>
|
||||
|
|
|
@ -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) {}
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -217,6 +217,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(',') : [];
|
||||
}
|
||||
|
|
|
@ -336,10 +336,6 @@ footer {
|
|||
bottom: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
min-height: calc(100% - 136px);
|
||||
}
|
||||
|
||||
material-footer {
|
||||
padding: 0.5em 1em;
|
||||
li {
|
||||
|
|
Loading…
Reference in New Issue