mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2025-04-29 16:39:54 +03:00
feat(error-page): add a full page for specific errors
This may help people to save their issues fixes #230
This commit is contained in:
parent
b9a157c943
commit
6314e8b11e
5 changed files with 94 additions and 12 deletions
|
@ -69,6 +69,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
state.registryUrl = props.registryUrl;
|
||||
let repositories = [];
|
||||
const self = this;
|
||||
const catalogUrl = `${props.registryUrl}/v2/_catalog?n=${state.catalogElementsLimit}`;
|
||||
const oReq = new Http({
|
||||
onAuthentication: this.props.onAuthentication,
|
||||
});
|
||||
|
@ -93,7 +94,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
return acc;
|
||||
}, []);
|
||||
} else if (this.status === 404) {
|
||||
self.props.onNotify('Server not found', true);
|
||||
self.props.onNotify({ code: 'CATALOG_NOT_FOUND', url: catalogUrl }, true);
|
||||
} else {
|
||||
self.props.onNotify(this.responseText);
|
||||
}
|
||||
|
@ -109,7 +110,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
loadend: true,
|
||||
});
|
||||
});
|
||||
oReq.open('GET', `${props.registryUrl}/v2/_catalog?n=${state.catalogElementsLimit}`);
|
||||
oReq.open('GET', catalogUrl);
|
||||
oReq.send();
|
||||
},
|
||||
};
|
||||
|
|
|
@ -109,9 +109,13 @@
|
|||
'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json'
|
||||
);
|
||||
oReq.addEventListener('error', function () {
|
||||
const credMsg = this.withCredentials
|
||||
? ' When you use credentials on a different hostname, the registry server may fail preflight requests. Check FAQ and issue #104.'
|
||||
: '';
|
||||
onNotify({
|
||||
message:
|
||||
"An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: ['DELETE'].",
|
||||
"An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: ['DELETE']." +
|
||||
credMsg,
|
||||
isError: true,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -80,6 +80,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
on-authenticated="{ state.onAuthenticated }"
|
||||
opened="{ state.authenticationDialogOpened }"
|
||||
></registry-authentication>
|
||||
<error-page
|
||||
if="{ state.pageError }"
|
||||
code="{ state.pageError.code }"
|
||||
status="{ state.pageError.status }"
|
||||
message="{ state.pageError.message }"
|
||||
url="{ state.pageError.url }"
|
||||
></error-page>
|
||||
<material-snackbar message="{ state.snackbarMessage }" is-error="{ state.snackbarIsError }"></material-snackbar>
|
||||
</main>
|
||||
<footer>
|
||||
|
@ -105,6 +112,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
import TagHistory from './tag-history/tag-history.riot';
|
||||
import DialogsMenu from './dialogs/dialogs-menu.riot';
|
||||
import SearchBar from './search-bar.riot';
|
||||
import ErrorPage from './error-page.riot';
|
||||
import { stripHttps, getRegistryServers, setRegistryServers, truthy, stringToArray } from '../scripts/utils';
|
||||
import router from '../scripts/router';
|
||||
import { loadTheme } from '../scripts/theme';
|
||||
|
@ -118,6 +126,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
SearchBar,
|
||||
Router,
|
||||
Route,
|
||||
ErrorPage,
|
||||
},
|
||||
onUpdated(props, state) {
|
||||
state.snackbarIsError = false;
|
||||
|
@ -184,6 +193,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
snackbarMessage: message,
|
||||
snackbarIsError: isError || false,
|
||||
});
|
||||
} else if (message && message.code) {
|
||||
this.update({
|
||||
pageError: message,
|
||||
});
|
||||
setTimeout(() => delete this.state['pageError'], 1000);
|
||||
} else if (message && message.message) {
|
||||
this.update({
|
||||
snackbarMessage: message.message,
|
||||
|
|
69
src/components/error-page.riot
Normal file
69
src/components/error-page.riot
Normal file
|
@ -0,0 +1,69 @@
|
|||
<error-page>
|
||||
<div class="content">
|
||||
<h1 if="{ getStatusCode() }">{ getStatusCode() }</h1>
|
||||
<h2>{ props.code }</h2>
|
||||
<template if="{ props.code === 'CATALOG_NOT_FOUND' }">
|
||||
<p>We received a 404 status code from your registry.</p>
|
||||
<p>The contact point was <a href="{ props.url }">{ props.url }</a></p>
|
||||
<p>
|
||||
This may be caused by a misconfiguration of Docker Registry UI. Check the
|
||||
<a href="https://joxit.dev/docker-registry-ui/#faq">FAQ</a> and
|
||||
<a href="https://joxit.dev/docker-registry-ui/#available-options">Available options</a>
|
||||
</p>
|
||||
</template>
|
||||
<template if="{ props.code === 'MIXED_CONTENT' }">
|
||||
<p>
|
||||
<span>Mixed Content</span>: The page at `<a href="{ window.location.origin }">{ window.location.origin }</a>`
|
||||
was loaded over HTTPS, but requested an insecure server endpoint `<a href="{ new URL(props.url).origin }"
|
||||
>{ new URL(props.url).origin }</a
|
||||
>`.
|
||||
</p>
|
||||
<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
|
||||
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>
|
||||
</template>
|
||||
<template if="{ props.code === 'INCORRECT_URL' }">
|
||||
<p>`{ props.url }` does not seems to be a correct URL, should starts with http:// or https://.</p>
|
||||
</template>
|
||||
</div>
|
||||
<script>
|
||||
export default {
|
||||
getStatusCode() {
|
||||
const { props } = this;
|
||||
switch (props.code) {
|
||||
case 'CATALOG_NOT_FOUND':
|
||||
return '404';
|
||||
}
|
||||
},
|
||||
URL: window.URL,
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 20px;
|
||||
}
|
||||
:host .content {
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
:host .content a {
|
||||
color: var(--accent-text);
|
||||
}
|
||||
:host .content a:visited {
|
||||
color: var(--accent-text);
|
||||
}
|
||||
:host .content p span {
|
||||
color: var(--accent-text);
|
||||
font-weight: 700;
|
||||
}
|
||||
:host .content h2 {
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
</error-page>
|
|
@ -71,7 +71,7 @@ export class Http {
|
|||
req.withCredentials = true;
|
||||
}
|
||||
req.hasHeader = hasHeader;
|
||||
req.getErrorMessage = Http.getErrorMessage;
|
||||
req.getErrorMessage = getErrorMessage;
|
||||
self.oReq = req;
|
||||
req.send();
|
||||
});
|
||||
|
@ -128,15 +128,9 @@ const hasHeader = function (header) {
|
|||
|
||||
const getErrorMessage = function () {
|
||||
if (this._url.match('^http://') && window.location.protocol === 'https:') {
|
||||
return (
|
||||
'Mixed Content: The page at `' +
|
||||
window.location.origin +
|
||||
'` was loaded over HTTPS, but requested an insecure server endpoint `' +
|
||||
new URL(this._url).origin +
|
||||
'`. This request has been blocked; the content must be served over HTTPS.'
|
||||
);
|
||||
return { code: 'MIXED_CONTENT', url: this._url };
|
||||
} else if (!this._url || !this._url.match('^http')) {
|
||||
return 'Incorrect server endpoint.';
|
||||
return { code: 'INCORRECT_URL', url: this._url };
|
||||
} else if (this.withCredentials && !this.hasHeader('Access-Control-Allow-Credentials')) {
|
||||
return (
|
||||
"The `Access-Control-Allow-Credentials` header in the response is missing and must be set to `true` when the request's credentials mode is on. Origin `" +
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue