Skip to content

Commit 41d5e6d

Browse files
committed
feat(cache-control): new option USE_CONTROL_CACHE_HEADER adds Cache-Control header on requests to registry server
This requires a change in your registry server configuration
1 parent 57a1cf9 commit 41d5e6d

File tree

7 files changed

+22
-6
lines changed

7 files changed

+22
-6
lines changed

README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ Some env options are available for use this interface for **only one server**.
110110
- `READ_ONLY_REGISTRIES`: Desactivate dialog for remove and add new registries, available only when `SINGLE_REGISTRY=false`. (default: `false`).
111111
- `SHOW_CATALOG_NB_TAGS`: Show number of tags per images on catalog page. This will produce + nb images requests, not recommended on large registries. (default: `false`).
112112
- `HISTORY_CUSTOM_LABELS`: Expose custom labels in history page, custom labels will be processed like maintainer label.
113+
- `USE_CONTROL_CACHE_HEADER`: Use `Control-Cache` header and set to `no-store, no-cache`. This will avoid some issues on multi-arch images (see [#260](https://github.com/Joxit/docker-registry-ui/issues/260)). This option requires registry configuration: `Access-Control-Allow-Headers` with `Cache-Control`. (default: `false`).
113114

114115
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/).
115116

@@ -128,7 +129,7 @@ http:
128129
headers:
129130
Access-Control-Allow-Origin: ['http://registry.example.com']
130131
Access-Control-Allow-Credentials: [true]
131-
Access-Control-Allow-Headers: ['Authorization', 'Accept']
132+
Access-Control-Allow-Headers: ['Authorization', 'Accept', 'Cache-Control']
132133
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS'] # Optional
133134
```
134135
@@ -150,7 +151,7 @@ And you need to add these HEADERS:
150151
http:
151152
headers:
152153
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
153-
Access-Control-Allow-Headers: ['Authorization', 'Accept']
154+
Access-Control-Allow-Headers: ['Authorization', 'Accept', 'Cache-Control']
154155
Access-Control-Expose-Headers: ['Docker-Content-Digest']
155156
```
156157
@@ -178,7 +179,7 @@ http:
178179
X-Content-Type-Options: [nosniff]
179180
Access-Control-Allow-Origin: ['http://127.0.0.1:8000']
180181
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
181-
Access-Control-Allow-Headers: ['Authorization', 'Accept']
182+
Access-Control-Allow-Headers: ['Authorization', 'Accept', 'Cache-Control']
182183
Access-Control-Max-Age: [1728000]
183184
Access-Control-Allow-Credentials: [true]
184185
Access-Control-Expose-Headers: ['Docker-Content-Digest']

bin/90-docker-registry-ui.sh

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ sed -i "s~\${DEFAULT_REGISTRIES}~${DEFAULT_REGISTRIES}~" index.html
1010
sed -i "s~\${READ_ONLY_REGISTRIES}~${READ_ONLY_REGISTRIES}~" index.html
1111
sed -i "s~\${SHOW_CATALOG_NB_TAGS}~${SHOW_CATALOG_NB_TAGS}~" index.html
1212
sed -i "s~\${HISTORY_CUSTOM_LABELS}~${HISTORY_CUSTOM_LABELS}~" index.html
13+
sed -i "s~\${USE_CONTROL_CACHE_HEADER}~${USE_CONTROL_CACHE_HEADER}~" index.html
1314

1415
if [ -z "${DELETE_IMAGES}" ] || [ "${DELETE_IMAGES}" = false ] ; then
1516
sed -i "s/\${DELETE_IMAGES}/false/" index.html

src/components/docker-registry-ui.riot

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
5252
on-notify="{ notifySnackbar }"
5353
filter-results="{ state.filter }"
5454
on-authentication="{ onAuthentication }"
55+
use-control-cache-header="{ truthy(props.useControlCacheHeader) }"
5556
></tag-list>
5657
</route>
5758
<route path="{baseRoute}taghistory/(.*)">
@@ -65,6 +66,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
6566
on-notify="{ notifySnackbar }"
6667
on-authentication="{ onAuthentication }"
6768
history-custom-labels="{ stringToArray(props.historyCustomLabels) }"
69+
use-control-cache-header="{ truthy(props.useControlCacheHeader) }"
6870
></tag-history>
6971
</route>
7072
</router>
@@ -133,6 +135,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
133135
this.state.name = props.name || stripHttps(props.registryUrl);
134136
this.state.catalogElementsLimit = props.catalogElementsLimit || 100000;
135137
this.state.pullUrl = this.pullUrl(this.state.registryUrl, props.pullUrl);
138+
this.state.useControlCacheHeader = props.useControlCacheHeader;
136139
},
137140
onServerChange(registryUrl) {
138141
this.update({

src/components/tag-history/tag-history.riot

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
5757
registryUrl: props.registryUrl,
5858
onNotify: props.onNotify,
5959
onAuthentication: props.onAuthentication,
60+
useControlCacheHeader: props.useControlCacheHeader,
6061
});
6162
state.image.fillInfo();
6263
},
@@ -66,14 +67,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
6667
},
6768
onTabChanged(arch, idx) {
6869
const state = this.state;
69-
const { registryUrl, onNotify } = this.props;
70+
const { registryUrl, onNotify, useControlCacheHeader } = this.props;
7071
state.elements = [];
7172
state.image.variants[idx] =
7273
state.image.variants[idx] ||
7374
new DockerImage(this.props.image, arch.digest, {
7475
list: false,
7576
registryUrl,
7677
onNotify,
78+
useControlCacheHeader,
7779
});
7880
if (state.image.variants[idx].blobs) {
7981
return this.processBlobs(state.image.variants[idx].blobs);

src/components/tag-list/tag-list.riot

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
9595
registryUrl: props.registryUrl,
9696
onNotify: props.onNotify,
9797
onAuthentication: props.onAuthentication,
98+
useControlCacheHeader: props.useControlCacheHeader,
9899
})
99100
)
100101
.sort(compare);

src/index.html

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
read-only-registries="${READ_ONLY_REGISTRIES}"
4747
show-catalog-nb-tags="${SHOW_CATALOG_NB_TAGS}"
4848
history-custom-labels="${HISTORY_CUSTOM_LABELS}"
49+
use-control-cache-header="${USE_CONTROL_CACHE_HEADER}"
4950
>
5051
</docker-registry-ui>
5152
<!-- endbuild -->
@@ -60,6 +61,7 @@
6061
single-registry="false"
6162
show-catalog-nb-tags="true"
6263
history-custom-labels="first_custom_labels,second_custom_labels"
64+
use-control-cache-header="false"
6365
>
6466
</docker-registry-ui>
6567
<!-- endbuild -->

src/scripts/docker-image.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export function compare(e1, e2) {
4646
}
4747

4848
export class DockerImage {
49-
constructor(name, tag, { list, registryUrl, onNotify, onAuthentication }) {
49+
constructor(name, tag, { list, registryUrl, onNotify, onAuthentication, useControlCacheHeader }) {
5050
this.name = name;
5151
this.tag = tag;
5252
this.chars = 0;
@@ -55,6 +55,7 @@ export class DockerImage {
5555
registryUrl,
5656
onNotify,
5757
onAuthentication,
58+
useControlCacheHeader,
5859
};
5960
this.ociImage = false;
6061
observable(this);
@@ -143,6 +144,9 @@ export class DockerImage {
143144
'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json' +
144145
(self.opts.list ? ', application/vnd.docker.distribution.manifest.list.v2+json' : '')
145146
);
147+
if (self.opts.useControlCacheHeader) {
148+
oReq.setRequestHeader('Cache-Control', 'no-store, no-cache');
149+
}
146150
oReq.send();
147151
}
148152
getBlobs(blob) {
@@ -165,7 +169,9 @@ export class DockerImage {
165169
self.trigger('creation-date', self.creationDate);
166170
self.trigger('blobs', self.blobs);
167171
} else if (this.status === 404) {
168-
self.opts.onNotify(`Blobs for ${self.name}:${self.tag} not found`, true);
172+
self.opts.onNotify(`Blobs for ${self.name}:${self.tag} not found: blob '${self.blobs}'`, true);
173+
} else if (!this.responseText) {
174+
self.opts.onNotify(`Can"t get blobs for ${self.name}:${self.tag}: blob '${self.blobs}' (no message error)`, true);
169175
} else {
170176
self.opts.onNotify(this.responseText);
171177
}

0 commit comments

Comments
 (0)