mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2026-02-19 21:29:51 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de6d09c98c | ||
|
|
1f2913248e | ||
|
|
3414d7b517 | ||
|
|
cd99f6e231 | ||
|
|
f015187b14 | ||
|
|
07713f1425 | ||
|
|
f560025e70 | ||
|
|
bf9c6c82e7 | ||
|
|
c74a9aeaa3 | ||
|
|
42bcec50df | ||
|
|
18dd5ca129 | ||
|
|
84b31f2cfb | ||
|
|
16d01d4dbf | ||
|
|
834a0ea40a |
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -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
19
Developing.md
Normal 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.
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -167,9 +167,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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
2
dist/docker-registry-ui.css
vendored
2
dist/docker-registry-ui.css
vendored
File diff suppressed because one or more lines are too long
2
dist/docker-registry-ui.js
vendored
2
dist/docker-registry-ui.js
vendored
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 |
@@ -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`.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -31,7 +31,8 @@ server {
|
||||
#! proxy_http_version 1.1;
|
||||
#! ${NGINX_PROXY_HEADERS}
|
||||
#! ${NGINX_PROXY_PASS_HEADERS}
|
||||
#! proxy_pass ${NGINX_PROXY_PASS_URL};
|
||||
#! set $registry_server "${NGINX_PROXY_PASS_URL}";
|
||||
#! proxy_pass $registry_server;
|
||||
#! }
|
||||
|
||||
#error_page 404 /404.html;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docker-registry-ui",
|
||||
"version": "2.5.0",
|
||||
"version": "2.5.4",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"format": "npm run format-html && npm run format-js && npm run format-riot",
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -336,10 +336,6 @@ footer {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
min-height: calc(100% - 136px);
|
||||
}
|
||||
|
||||
material-footer {
|
||||
padding: 0.5em 1em;
|
||||
li {
|
||||
|
||||
Reference in New Issue
Block a user