Compare commits

...

16 Commits
2.0.7 ... 2.1.0

Author SHA1 Message Date
Joxit
8ddfb1b5ae build: release 2.1.0 🚀 2021-11-09 23:07:12 +01:00
Joxit
3a385fc08d feat(nginx): add support for proxy_pass_header directive via NGINX_PROXY_PASS_HEADER_*
fixes #206
2021-11-08 05:28:39 +01:00
Joxit
f958365336 feat(search bar): add shortcuts CRTL + F or F3 to select the search bar
fixes #213
2021-11-06 14:50:18 +01:00
Joxit
29c17b1baa docs: add documentation + example for DEFAULT_REGISTRIES and READ_ONLY_REGISTRIES 2021-11-05 22:39:19 +01:00
Joxit
b323dc6c04 feat: add new option READ_ONLY_REGISTRIES available when SINGLE_REGISTRY=false 2021-11-04 00:48:33 +01:00
Joxit
992328eae9 fix: should update the catalog when the server is changed 2021-11-02 22:51:32 +01:00
Joxit
dd26bf66a2 feat: default registries is set only when there is no registries 2021-11-01 10:55:55 +01:00
Joxit
8fcae3cda4 feat: add option for default registries when SINGLE_REGISTRY=false 2021-10-31 15:09:37 +01:00
Joxit
f4455703ca docs: default value for SHOW_CONTENT_DIGEST is false since 2.0.0 2021-10-30 18:01:23 +02:00
Joxit
22e3f2254e docs: more issues for DELETE and CORS
fixes #207
fixes #214
2021-10-30 18:01:23 +02:00
Max H. Gerlach
4075e0005c docs: fix directory in electron install instructions (#208) 2021-10-16 08:57:12 +02:00
John Poth
7c00b85183 chore: update electron example (#210)
Add examples to electron path
Add run command
2021-10-16 08:40:09 +02:00
Joxit
21e3ad51af fix(warn): Refused to get unsafe header "www-authenticate" 2021-09-13 09:14:00 +02:00
Joxit
73613a3b96 fix: The tag seo is not a recognized Liquid tag. 2021-09-11 15:52:51 +02:00
Joxit
6da744a9c5 ci: add major and patch tag versions 2021-08-25 07:48:41 +02:00
Joxit
db6b74a5f0 fix(docker-image): minor typo for sha256
fixes #201
2021-07-13 23:32:57 +02:00
24 changed files with 218 additions and 68 deletions

View File

@@ -15,9 +15,15 @@ jobs:
run: npm install
- name: Build the interface
run: npm run build
- name: Current tag
id: current-tag
- name: Major tag
id: major-tag
run: echo "::set-output name=tag::$(git describe --tags | grep -o '^[0-9]*')"
- name: Minor tag
id: minor-tag
run: echo "::set-output name=tag::$(git describe --tags | grep -o '^[0-9]*\.[0-9]*')"
- name: Patch tag
id: patch-tag
run: echo "::set-output name=tag::$(git describe --tags | grep -o '^[0-9]*\.[0-9]*\.[0-9]*')"
- name: Download kokai
run: curl -sSL https://github.com/Joxit/kokai/releases/download/$(curl -sSL https://api.github.com/repos/Joxit/kokai/releases/latest | jq -r ".tag_name")/kokai-linux-x86_64 > kokai
- name: Create Release Note
@@ -51,7 +57,9 @@ jobs:
push: true
tags: |
joxit/docker-registry-ui:latest
joxit/docker-registry-ui:${{steps.current-tag.outputs.tag}}
joxit/docker-registry-ui:${{steps.major-tag.outputs.tag}}
joxit/docker-registry-ui:${{steps.minor-tag.outputs.tag}}
joxit/docker-registry-ui:${{steps.patch-tag.outputs.tag}}
- name: Build and push Latest Debian Version
uses: docker/build-push-action@v2
with:
@@ -61,4 +69,6 @@ jobs:
push: true
tags: |
joxit/docker-registry-ui:debian
joxit/docker-registry-ui:${{steps.current-tag.outputs.tag}}-debian
joxit/docker-registry-ui:${{steps.major-tag.outputs.tag}}-debian
joxit/docker-registry-ui:${{steps.minor-tag.outputs.tag}}-debian
joxit/docker-registry-ui:${{steps.patch-tag.outputs.tag}}-debian

View File

@@ -41,9 +41,13 @@ This web user interface uses [Riot](https://github.com/Riot/riot) the react-like
- Add Title when using `REGISTRY_TITLE` (see [#28](https://github.com/Joxit/docker-registry-ui/issues/28)).
- Customise docker pull command on static registry UI (see [#71](https://github.com/Joxit/docker-registry-ui/issues/71)).
- Add custom header via environment variable and file via `NGINX_PROXY_HEADER_*` (see [#89](https://github.com/Joxit/docker-registry-ui/pull/89))
- Show/Hide content digest in taglist via `SHOW_CONTENT_DIGEST` (values are: [`true`, `false`], default: `true`) (see [#126](https://github.com/Joxit/docker-registry-ui/issues/126)).
- Show/Hide content digest in taglist via `SHOW_CONTENT_DIGEST` (values are: [`true`, `false`], default: `false`) (see [#126](https://github.com/Joxit/docker-registry-ui/issues/126)).
- Limit the number of elements in the image list via `CATALOG_ELEMENTS_LIMIT` (see [#127](https://github.com/Joxit/docker-registry-ui/pull/127)).
- 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))
- Set a list of default registries with `DEFAULT_REGISTRIES` (see [#219](https://github.com/Joxit/docker-registry-ui/pull/219)).
- Desactivate add and remove regisitries with `READ_ONLY_REGISTRIES` (see [#219](https://github.com/Joxit/docker-registry-ui/pull/219)).
- Filter images and tags with a search bar. 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)).
- **Forward** custom header to your backend registry via environment variable and file via `NGINX_PROXY_PASS_HEADER_*` (see [#206](https://github.com/Joxit/docker-registry-ui/pull/206)).
## FAQ
@@ -64,7 +68,7 @@ This web user interface uses [Riot](https://github.com/Riot/riot) the react-like
- 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 DELETE fails with 401 status code (using Basic Auth) ?
- This is caused by a bug in docker registry, I suggest to have your UI on the same domain than your registry e.g. registry.example.com/ui/. (see [#104](https://github.com/Joxit/docker-registry-ui/issues/104)).
- This is caused by a bug in docker registry, I suggest to have your UI on the same domain than your registry and use `NGINX_PROXY_PASS_URL` e.g. registry.example.com/ui/. (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)).
- 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?
@@ -83,11 +87,14 @@ Some env options are available for use this interface for **only one server**.
- `REGISTRY_TITLE`: Set a custom title for your user interface. (default: value derived from `REGISTRY_URL`).
- `PULL_URL`: Set a custom url when you copy the `docker pull` command. (default: value derived from `REGISTRY_URL`).
- `DELETE_IMAGES`: Set if we can delete images from the UI. (default: `false`)
- `SHOW_CONTENT_DIGEST`: Show content digest in docker tag list. (default: `true`)
- `SHOW_CONTENT_DIGEST`: Show content digest in docker tag list. (default: `false`)
- `CATALOG_ELEMENTS_LIMIT`: Limit the number of elements in the catalog page. (default: `100000`).
- `SINGLE_REGISTRY`: Remove the menu that show the dialogs to add, remove and change the endpoint of your docker registry. (default `false`)
- `SINGLE_REGISTRY`: Remove the menu that show the dialogs to add, remove and change the endpoint of your docker registry. (default: `false`).
- `NGINX_PROXY_PASS_URL`: Update the default Nginx configuration and set the **proxy_pass** to your backend docker registry (this avoid CORS configuration). This is usually the name of your registry container in the form `http://registry:5000`.
- `NGINX_PROXY_HEADER_*`: Update the default Nginx configuration and set **custom headers** for your backend docker registry. Only when `NGINX_PROXY_PASS_URL` is used.
- `NGINX_PROXY_HEADER_*`: Update the default Nginx configuration and **set custom headers** for your backend docker registry. Only when `NGINX_PROXY_PASS_URL` is used.
- `NGINX_PROXY_HEADER_*`: Update the default Nginx configuration and **forward custom headers** to your backend docker registry. Only when `NGINX_PROXY_PASS_URL` is used.
- `DEFAULT_REGISTRIES`: List of comma separated registry URLs (e.g `http://registry.example.com,http://registry:5000`), available only when `SINGLE_REGISTRY=false`. (default: ` `).
- `READ_ONLY_REGISTRIES`: Desactivate dialog for remove and add new registries, available only when `SINGLE_REGISTRY=false`. (default: `false`).
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/).
@@ -182,3 +189,4 @@ check out the [Electron](examples/electron/README.md) standalone application.
- [UI showing same sha256 content digest for all tags + Delete is not working](https://github.com/Joxit/docker-registry-ui/tree/main/examples/issue-116) ([#116](https://github.com/Joxit/docker-registry-ui/issues/116))
- [Electron-based Standalone Application](https://github.com/Joxit/docker-registry-ui/tree/main/examples/electron) ([#129](https://github.com/Joxit/docker-registry-ui/pull/129))
- [Use docker-registry-ui as proxy with read-only right](https://github.com/Joxit/docker-registry-ui/tree/main/examples/read-only-auth) ([#47](https://github.com/Joxit/docker-registry-ui/issues/47))
- [Use DEFAULT_REGISTRIES and READ_ONLY_REGISTRIES](https://github.com/Joxit/docker-registry-ui/tree/main/examples/pr-219) ([#219](https://github.com/Joxit/docker-registry-ui/issues/219))

View File

@@ -12,4 +12,6 @@ defaults:
- scope:
path: ""
values:
image: /screenshot.png
image: /screenshot.png
plugins:
- jekyll-seo-tag

View File

@@ -1,11 +1,13 @@
#!/bin/sh
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~\${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~\${DEFAULT_REGISTRIES}~${DEFAULT_REGISTRIES}~" index.html
sed -i "s~\${READ_ONLY_REGISTRIES}~${READ_ONLY_REGISTRIES}~" index.html
if [ -z "${DELETE_IMAGES}" ] || [ "${DELETE_IMAGES}" = false ] ; then
sed -i "s/\${DELETE_IMAGES}/false/" index.html
@@ -30,8 +32,25 @@ get_nginx_proxy_headers() {
done
}
get_nginx_proxy_pass_headers() {
(
env &&
if [ -f "/etc/nginx/.env" ]; then
cat /etc/nginx/.env
# Force new line
echo ""
fi
) | while read e; do
if [ -n "$(echo $e | grep -o '^NGINX_PROXY_PASS_HEADER_')" ]; then
key=$(echo ${e%%=*} | sed 's/^NGINX_PROXY_PASS_HEADER_//' | sed 's/_/-/g')
echo -n "proxy_pass_header \"${key}\"; "
fi
done
}
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
fi

View File

@@ -37,7 +37,7 @@
<body>
<docker-registry-ui registry-url="" name="Demo Docker Registry UI" pull-url="" show-content-digest="true"
is-image-remove-activated="true" catalog-elements-limit="1000" single-registry="false">
is-image-remove-activated="true" catalog-elements-limit="1000" single-registry="false" default-registries="https://joxit.dev/docker-registry-demo">
<script>
if (localStorage.getItem('registryServer')) {
localStorage.setItem('registryServer',
@@ -46,10 +46,6 @@
'https://joxit.dev/docker-registry-demo'
)
)
} else {
localStorage.setItem('registryServer', JSON.stringify([
'https://joxit.dev/docker-registry-demo'
]))
}
</script>
<script src="../dist/docker-registry-ui.js"></script>

File diff suppressed because one or more lines are too long

3
dist/index.html vendored
View File

@@ -15,4 +15,5 @@
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 registry-url="${REGISTRY_URL}" name="${REGISTRY_TITLE}" pull-url="${PULL_URL}"
show-content-digest="${SHOW_CONTENT_DIGEST}" is-image-remove-activated="${DELETE_IMAGES}"
catalog-elements-limit="${CATALOG_ELEMENTS_LIMIT}" single-registry="${SINGLE_REGISTRY}"></docker-registry-ui><script src="docker-registry-ui.js"></script></body></html>
catalog-elements-limit="${CATALOG_ELEMENTS_LIMIT}" single-registry="${SINGLE_REGISTRY}"
default-registries="${DEFAULT_REGISTRIES}" read-only-registries="${READ_ONLY_REGISTRIES}"></docker-registry-ui><script src="docker-registry-ui.js"></script></body></html>

View File

@@ -10,4 +10,5 @@
- [Add custom headers bases on environment variable and/or file when the ui is used as proxy](https://github.com/Joxit/docker-registry-ui/tree/main/examples/proxy-headers) ([#89](https://github.com/Joxit/docker-registry-ui/pull/89))
- [UI showing same sha256 content digest for all tags + Delete is not working](https://github.com/Joxit/docker-registry-ui/tree/main/examples/issue-116) ([#116](https://github.com/Joxit/docker-registry-ui/issues/116))
- [Electron-based Standalone Application](https://github.com/Joxit/docker-registry-ui/tree/main/examples/electron) ([#129](https://github.com/Joxit/docker-registry-ui/pull/129))
- [Use docker-registry-ui as proxy with read-only right](https://github.com/Joxit/docker-registry-ui/tree/main/examples/read-only-auth) ([#47](https://github.com/Joxit/docker-registry-ui/issues/47))
- [Use docker-registry-ui as proxy with read-only right](https://github.com/Joxit/docker-registry-ui/tree/main/examples/read-only-auth) ([#47](https://github.com/Joxit/docker-registry-ui/issues/47))
- [Use DEFAULT_REGISTRIES and READ_ONLY_REGISTRIES](https://github.com/Joxit/docker-registry-ui/tree/main/examples/pr-219) ([#219](https://github.com/Joxit/docker-registry-ui/issues/219))

View File

@@ -16,10 +16,14 @@ computer.
* After building the web application, navigate to the ```electron``` directory
and execute following commands to build the executable:
```bash
cd electron
cd examples/electron
npm install
npm run dist
```
* Run the application:
```bash
npm start
```
If you encounter any issues, please check the troubleshooting below.

14
examples/pr-219/README.md Normal file
View File

@@ -0,0 +1,14 @@
# Example for pull request #219
Basic usage for `DEFAULT_REGISTRIES` and `READ_ONLY_REGISTRIES`.
Behaviors:
- `DEFAULT_REGISTRIES`:
- will set the list of registries in the localstorage when the localstorage is empty.
- will overwrite the list of registries every time when `READ_ONLY_REGISTRIES=true`
- `READ_ONLY_REGISTRIES`:
- will remove dialog for Add and Remove registries
These options works only when `SINGLE_REGISTRY=false`
See [#219](https://github.com/Joxit/docker-registry-ui/pull/219)

View File

@@ -0,0 +1,39 @@
version: '2'
services:
registry_1:
image: registry:latest
restart: always
ports:
- 5000:5000
container_name: registry_1
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: "['*']"
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
volumes:
- ./data:/var/lib/registry
registry_2:
image: registry:latest
restart: always
ports:
- 5001:5000
container_name: registry_2
environment:
REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: "['*']"
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
volumes:
- ./data:/var/lib/registry
ui:
image: joxit/docker-registry-ui:latest
restart: always
container_name: registry-ui
environment:
- REGISTRY_TITLE=Private Docker Registry
- DEFAULT_REGISTRIES=http://localhost:5000,http://localhost:5001
- DELETE_IMAGES=true
- READ_ONLY_REGISTRIES=true
- SINGLE_REGISTRY=false
ports:
- 80:80

View File

@@ -27,6 +27,7 @@ server {
#! return 404;
#! }
#! ${NGINX_PROXY_HEADERS}
#! ${NGINX_PROXY_PASS_HEADERS}
#! proxy_pass ${NGINX_PROXY_PASS_URL};
#! }

View File

@@ -1,6 +1,6 @@
{
"name": "docker-registry-ui",
"version": "2.0.7",
"version": "2.1.0",
"scripts": {
"start": "ROLLUP_SERVE=true rollup -c -w",
"build": "rollup -c",
@@ -14,21 +14,21 @@
"license": "AGPL-3.0",
"description": "A web UI for private docker registry",
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/preset-env": "^7.12.7",
"@riotjs/compiler": "^5.3.1",
"@riotjs/observable": "^4.0.4",
"@riotjs/route": "^7.0.0",
"@babel/core": "^7.16.0",
"@babel/preset-env": "^7.16.0",
"@riotjs/compiler": "^5.4.2",
"@riotjs/observable": "^4.1.1",
"@riotjs/route": "^7.1.2",
"@rollup/plugin-babel": "^5.2.2",
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-html": "^0.2.3",
"@rollup/plugin-html": "^0.2.4",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"core-js": "^3.9.1",
"js-beautify": "^1.13.0",
"riot": "^5.3.1",
"riot-mui": "joxit/riot-5-mui#4d68d7f",
"rollup": "^2.34.2",
"@rollup/plugin-node-resolve": "^11.2.1",
"core-js": "^3.19.1",
"js-beautify": "^1.14.0",
"riot": "^5.4.5",
"riot-mui": "github:joxit/riot-5-mui#4d68d7f",
"rollup": "^2.59.0",
"rollup-plugin-app-utils": "^1.0.6",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0",

View File

@@ -26,12 +26,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div if="{ !state.loadend }" class="spinner-wrapper">
<material-spinner></material-spinner>
</div>
<catalog-element each="{ item in state.repositories }" item="{ item }" filter-results="{ props.filterResults }"/>
<catalog-element each="{ item in state.repositories }" item="{ item }" filter-results="{ props.filterResults }" />
<script>
import CatalogElement from './catalog-element.riot'
import {
Http
} from '../../scripts/http';
import {
getRegistryServers
} from '../../scripts/utils';
export default {
components: {
@@ -41,18 +44,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
registryName: '',
length: 0,
loadend: false,
repositories: []
repositories: [],
registryUrl: ''
},
onBeforeMount(props) {
this.state.registryName = props.registryName;
this.state.catalogElementsLimit = props.catalogElementsLimit;
},
onMounted(props) {
this.display(props, this.state)
onMounted(props, state) {
this.display(props, state)
},
onUpdated(props, state) {
this.display(props, state);
},
display(props, state) {
if (props.registryUrl === state.registryUrl) {
return;
}
state.registryUrl = props.registryUrl;
let repositories = [];
const self = this;
const oReq = new Http({

View File

@@ -32,7 +32,7 @@
</material-popup>
<script>
import {
getRegistryServers
addRegistryServers
} from '../../scripts/utils';
import router from '../../scripts/router';
@@ -51,9 +51,7 @@
if (!input.value.startsWith('http')) {
return this.props.onNotify('The input field should start with http:// or https://.', true);
}
const url = input.value.trim().replace(/\/*$/, '');
const registryServer = getRegistryServers().filter(e => e !== url);
localStorage.setItem('registryServer', JSON.stringify([url].concat(registryServer)));
const url = addRegistryServers(input.value);
router.home()
this.props.onServerChange(url);
this.props.onClose()

View File

@@ -33,6 +33,7 @@
</material-popup>
<script>
import {
addRegistryServers,
getRegistryServers
} from '../../scripts/utils';
import router from '../../scripts/router';
@@ -45,9 +46,7 @@
if (!select.value.startsWith('http')) {
return this.props.onNotify('The select field should start with http:// or https://.', true);
}
const url = select.value.trim().replace(/\/*$/, '');
const registryServer = getRegistryServers().filter(e => e !== url);
localStorage.setItem('registryServer', JSON.stringify([url].concat(registryServer)));
const url = addRegistryServers(select.value);
router.home()
this.props.onServerChange(url);
this.props.onClose()

View File

@@ -15,17 +15,17 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<dialogs-menu>
<add-registry-url opened="{ state['add-registry-url'] }" on-close="{ onClose('add-registry-url') }"
<add-registry-url if="{ !props.readOnlyRegistries }" opened="{ state['add-registry-url'] }" on-close="{ onClose('add-registry-url') }"
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></add-registry-url>
<change-registry-url opened="{ state['change-registry-url'] }" on-close="{ onClose('change-registry-url') }"
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></change-registry-url>
<remove-registry-url opened="{ state['remove-registry-url'] }" on-close="{ onClose('remove-registry-url') }"
<remove-registry-url if="{ !props.readOnlyRegistries }" opened="{ state['remove-registry-url'] }" on-close="{ onClose('remove-registry-url') }"
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></remove-registry-url>
<div class="container">
<material-button onClick="{ onClick }" waves-center="true" rounded="true" waves-opacity="0.6" waves-duration="600">
<i class="material-icons">more_vert</i>
</material-button>
<material-dropdown-list items="{ dropdownItems }" onSelect="{ onDropdownSelect }"
<material-dropdown-list items="{ dropdownItems.filter(item => item.ro || !props.readOnlyRegistries) }" onSelect="{ onDropdownSelect }"
opened="{ state.isDropdownOpened }" />
</div>
<div class="overlay" onclick="{ onClick }" if="{ state.isDropdownOpened }"></div>
@@ -42,13 +42,16 @@
},
dropdownItems: [{
title: 'Add URL',
name: 'add-registry-url'
name: 'add-registry-url',
ro: false
}, {
title: 'Change URL',
name: 'change-registry-url'
name: 'change-registry-url',
ro: true
}, {
title: 'Remove URL',
name: 'remove-registry-url'
name: 'remove-registry-url',
ro: false
}],
onDropdownSelect(key, item) {
this.update({

View File

@@ -38,13 +38,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</material-popup>
<script>
import {
getRegistryServers
getRegistryServers,
removeRegistryServers
} from '../../scripts/utils';
export default {
remove(event) {
const url = event.currentTarget.attributes.url && event.currentTarget.attributes.url.value;
const registryServer = getRegistryServers().filter(e => e !== url);
localStorage.setItem('registryServer', JSON.stringify(registryServer));
removeRegistryServers(url);
setTimeout(() => this.update(), 100);
},
getRegistryServers

View File

@@ -20,7 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="logo">Docker Registry UI</div>
<search-bar on-search="{ onSearch }"></search-bar>
<dialogs-menu if="{props.singleRegistry !== 'true'}" on-notify="{ notifySnackbar }"
on-server-change="{ onServerChange }"></dialogs-menu>
on-server-change="{ onServerChange }" default-registries="{ props.defaultRegistries }"
read-only-registries="{ truthy(props.readOnlyRegistries) }"></dialogs-menu>
</material-navbar>
</header>
<main>
@@ -77,6 +78,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import {
stripHttps,
getRegistryServers,
setRegistryServers,
truthy
} from '../scripts/utils';
import router from '../scripts/router';
@@ -96,6 +98,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
state.snackbarMessage = undefined;
},
onBeforeMount(props) {
if ((props.defaultRegistries && props.defaultRegistries.length > 0 && getRegistryServers().length === 0) ||
truthy(props.readOnlyRegistries)) {
setRegistryServers(props.defaultRegistries);
}
// props.singleRegistry === 'true' means old static version
const registryUrl = props.registryUrl ||
(props.singleRegistry === 'true' ? undefined : (router.getUrlQueryParam() || getRegistryServers(0))) ||

View File

@@ -20,6 +20,18 @@
input.value = '';
notify();
})
window.addEventListener('keydown', e => {
// F3 or CTRL + F
if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) {
// already focused, fallback to default behavior
if (document.activeElement === input) {
return true;
} else {
e.preventDefault();
input.focus();
}
}
})
}
}

View File

@@ -37,7 +37,8 @@
<!-- build:keep production -->
<docker-registry-ui registry-url="${REGISTRY_URL}" name="${REGISTRY_TITLE}" pull-url="${PULL_URL}"
show-content-digest="${SHOW_CONTENT_DIGEST}" is-image-remove-activated="${DELETE_IMAGES}"
catalog-elements-limit="${CATALOG_ELEMENTS_LIMIT}" single-registry="${SINGLE_REGISTRY}">
catalog-elements-limit="${CATALOG_ELEMENTS_LIMIT}" single-registry="${SINGLE_REGISTRY}"
default-registries="${DEFAULT_REGISTRIES}" read-only-registries="${READ_ONLY_REGISTRIES}">
</docker-registry-ui>
<!-- endbuild -->
<!-- build:keep developement -->

View File

@@ -64,7 +64,7 @@ export class DockerImage {
return this.fillInfo();
});
this.on('get-sha256', function () {
if (this.size !== undefined) {
if (this.sha256 !== undefined) {
return this.trigger('sha256', this.sha256);
}
return this.fillInfo();

View File

@@ -53,7 +53,8 @@ export class Http {
case 'loadend': {
self.oReq.addEventListener('loadend', function () {
if (this.status == 401 && !this.withCredentials) {
const tokenAuth = parseAuthenticateHeader(this.getResponseHeader('www-authenticate'));
const tokenAuth =
this.hasHeader('www-authenticate') && parseAuthenticateHeader(this.getResponseHeader('www-authenticate'));
self.onAuthentication(tokenAuth, (bearer) => {
const req = new XMLHttpRequest();
req._url = self._url;
@@ -65,7 +66,7 @@ export class Http {
req.setRequestHeader(key, self._headers[key]);
}
if (bearer && bearer.token) {
req.setRequestHeader('Authorization', `Bearer ${bearer.token}`)
req.setRequestHeader('Authorization', `Bearer ${bearer.token}`);
} else {
req.withCredentials = true;
}

View File

@@ -1,3 +1,5 @@
const LOCAL_STORAGE_KEY = 'registryServer';
export function bytesToSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == undefined || isNaN(bytes)) {
@@ -152,7 +154,7 @@ export const ERROR_CAN_NOT_READ_CONTENT_DIGEST = {
export function getRegistryServers(i) {
try {
const res = JSON.parse(localStorage.getItem('registryServer'));
const res = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY));
if (res instanceof Array) {
return !isNaN(i) ? res[i] : res.map((url) => url.trim().replace(/\/*$/, ''));
}
@@ -160,6 +162,28 @@ export function getRegistryServers(i) {
return !isNaN(i) ? '' : [];
}
export function setRegistryServers(registries) {
if (typeof registries === 'string') {
registries = registries.split(',');
} else if (!Array.isArray(registries)) {
throw new Error('setRegistries must be called with string or array parameter');
}
registries = registries.map((registry) => registry.replace(/\/*$/, ''));
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(registries));
}
export function addRegistryServers(registry) {
const url = registry.trim().replace(/\/*$/, '');
const registryServer = getRegistryServers().filter((e) => e !== url);
setRegistryServers([url].concat(registryServer));
return url;
}
export function removeRegistryServers(registry) {
const registryServers = getRegistryServers().filter((e) => e !== registry);
setRegistryServers(registryServers);
}
export function encodeURI(url) {
if (!url) {
return;
@@ -175,5 +199,5 @@ export function decodeURI(url) {
}
export function truthy(value) {
return value === true || value === "true";
}
return value === true || value === 'true';
}