Compare commits

...

15 Commits
2.5.0 ... 2.5.5

Author SHA1 Message Date
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
Joxit
f015187b14 fix: scrollbar always displayed on safari (#327)
fixes #327
2023-07-23 19:22:49 +02:00
Joxit
07713f1425 docs: add information on how to run development environment (#314)
fixes #314
2023-07-23 00:02:31 +02:00
Joxit
f560025e70 docs(templates): update bug report issue template 2023-07-20 18:35:24 +02:00
Joxit
bf9c6c82e7 fix: shift-click for multi-delete-in-a-row selects wrong images (#323)
fixes #323
2023-07-18 00:11:09 +02:00
Joxit
c74a9aeaa3 chore(version-notification): update latest version management 2023-07-17 18:16:42 +02:00
cui fliter
42bcec50df docs: fix some typo (#324)
Signed-off-by: cui fliter <imcusg@gmail.com>
2023-07-14 07:51:54 +02:00
Joxit
18dd5ca129 docs: update project GIF for the new UI 2023-06-19 06:35:19 +02:00
Joxit
84b31f2cfb docs: fix typo in README 2023-06-18 19:49:27 +02:00
Joxit
16d01d4dbf fix(demo): add taglist-order to fix taglist page 2023-06-17 23:55:34 +02:00
Joxit
834a0ea40a ci: use nginx:alpine-slim docker image 2023-06-16 23:32:03 +02:00
22 changed files with 103 additions and 62 deletions

View File

@@ -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

19
Developing.md Normal file
View File

@@ -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.

View File

@@ -12,7 +12,7 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
FROM nginx:alpine
FROM nginx:alpine-slim
LABEL maintainer="Jones MAGLOIRE @Joxit"

View File

@@ -45,7 +45,7 @@ If you like my work and want to support it, don't hesitate to [sponsor me](https
- You can select the search bar with the shortcut `CRTL + F` or `F3`. When the search bar is already focused, the shortcut will fallback to the default behavior (see [#213](https://github.com/Joxit/docker-registry-ui/issues/213)). Since 2.1.0
- Multi arch support in history page (see [#130](https://github.com/Joxit/docker-registry-ui/issues/130) and [#134](https://github.com/Joxit/docker-registry-ui/pull/134)). Since 1.5.0
- Show the content of the dockerfile (see [#286](https://github.com/Joxit/docker-registry-ui/pull/286)). Since 2.4.0
- The UI will cache requests from your registry, such as blobs and some manifets (URL with `sha256:`).
- The UI will cache requests from your registry, such as blobs and some manifests (URL with `sha256:`).
Checkout all options in [Available options](#available-options) section.
@@ -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
@@ -167,9 +170,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'

View File

@@ -65,7 +65,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

View File

@@ -12,7 +12,7 @@ for i in arm64v8 arm32v7 master latest debian main; do
docker push 127.0.0.1:5000/joxit/docker-registry-ui:$i
done
for v in 1.1 1.2 1.3 1.4 1.5 2.0 2 2.0.0 2.1 2.1.0 2.2 2.2.0 2.3 2.3.0 2.4 2.4.0; do
for v in 1.1 1.2 1.3 1.4 1.5 2.0 2 2.0.0 2.1 2.1.0 2.2 2.2.0 2.3 2.3.0 2.4 2.4.0 2.5.0; do
for type in "-debian" ""; do
docker pull joxit/docker-registry-ui:$v$type
docker tag joxit/docker-registry-ui:$v$type 127.0.0.1:5000/joxit/docker-registry-ui:$v$type

View File

@@ -49,6 +49,7 @@
single-registry="false"
default-registries="https://joxit.dev/docker-registry-demo"
show-catalog-nb-tags="true"
taglist-order=""
theme="auto"
/>
<script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

After

Width:  |  Height:  |  Size: 3.6 MiB

View File

@@ -6,5 +6,5 @@ There are two htpasswd files. `read-write.htpasswd` a read and write access to t
All users in `read-only.htpasswd` should be in `read-write.htpasswd`.
Read only user: login: `read` password: `regisrty`.
Read and write user: login: `write` password: `regisrty`.
Read only user: login: `read` password: `registry`.
Read and write user: login: `write` password: `registry`.

View File

@@ -12,7 +12,7 @@ bash run-swarm.sh
## Authentication
The registry is protected via __Basic authentication__ but feel free to use wathever you like.
The registry is protected via __Basic authentication__ but feel free to use whatever you like.
In this sample, credentials are: **admin / admin**.
To generate a new password for basic auth, run the command: `htpasswd -nb username password`.

View File

@@ -1,9 +1,9 @@
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
client_max_body_size 0;
@@ -31,10 +31,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 +44,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.0",
"version": "2.5.5",
"type": "module",
"scripts": {
"format": "npm run format-html && npm run format-js && npm run format-riot",

View File

@@ -30,7 +30,7 @@ const getVersion = (version) => {
return version;
};
fs.writeFileSync('.version.json', JSON.stringify({ version: getVersion(version) }));
fs.writeFileSync('.version.json', JSON.stringify({ version: getVersion(version), latest: version }));
const plugins = [
riot(),

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

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<material-navbar>
<span class="logo">
<span>Docker Registry UI</span>
<version-notification version="{ version }" on-notify="{ notifySnackbar }"></version-notification>
<version-notification version="{ latest }" on-notify="{ notifySnackbar }"></version-notification>
</span>
<div class="menu">
<search-bar on-search="{ onSearch }"></search-bar>
@@ -137,7 +137,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</material-footer>
</footer>
<script>
import { version } from '../../.version.json';
import { version, latest } from '../../.version.json';
import { Router, Route } from '@riotjs/route';
import Catalog from './catalog/catalog.riot';
import TagList from './tag-list/tag-list.riot';
@@ -260,11 +260,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
baseRoute: '([^#]*?)/(\\?[^#]*?)?(#!)?(/?)',
router,
version,
latest,
truthy,
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);

View File

@@ -238,7 +238,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
return selectedImage;
} else {
let shouldChange = false;
const tags = getPage(this.props.tags, this.props.page);
const tags = this.getPage(this.props.tags, this.props.page);
tags
.filter((image) => {
if (image == this.state.selectedImage || image == selectedImage) {

View File

@@ -3,7 +3,7 @@
<material-popup opened="{ state.open }" onClick="{ onClose }">
<div class="material-popup-title">Check for updates</div>
<div class="material-popup-content">
<p>The version <b>{ state.tag_name }</b> of Docker Regisrty UI now available.</p>
<p>The version <b>{ state.tag_name }</b> of Docker Registry UI now available.</p>
<p>You can download the lastest version with docker.</p>
<code>joxit/docker-registry-ui:{ state.tag_name }</code>
</div>

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

@@ -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

@@ -336,10 +336,6 @@ footer {
bottom: 0;
}
main {
min-height: calc(100% - 136px);
}
material-footer {
padding: 0.5em 1em;
li {