mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 08:22:24 +01:00
176 lines
5 KiB
JavaScript
176 lines
5 KiB
JavaScript
import { createElement, createRender } from "../lib/skeleton/index.js";
|
|
import { toHref, fromHref, navigate } from "../lib/skeleton/router.js";
|
|
import { forwardURLParams } from "../lib/path.js";
|
|
import rxjs, { effect, applyMutation } from "../lib/rx.js";
|
|
import { qs, safe } from "../lib/dom.js";
|
|
import t from "../locales/index.js";
|
|
|
|
import { AjaxError, ApplicationError } from "../lib/error.js";
|
|
|
|
import "../components/icon.js";
|
|
|
|
export default function(render) {
|
|
let hasBack = window.self === window.top;
|
|
if (!render) {
|
|
render = createRender(document.body);
|
|
try { render = createRender(qs(document.body, "[role=\"main\"]")); }
|
|
catch (err) { hasBack = false; }
|
|
}
|
|
|
|
return function(err) {
|
|
const [msg, trace] = processError(err);
|
|
|
|
const shouldRedirectLogin = err instanceof AjaxError && err.err().status === 401;
|
|
let link = "";
|
|
if (hasBack) {
|
|
link = forwardURLParams(calculateBacklink(fromHref(window.location.pathname)), ["share"]);
|
|
if (shouldRedirectLogin) {
|
|
link = fromHref("/login?next=" + encodeURIComponent(forwardURLParams(fromHref(window.location.pathname), ["share"])));
|
|
}
|
|
}
|
|
const $page = createElement(`
|
|
<div>
|
|
<style>${css}</style>
|
|
<a href="${link}" class="backnav ${!hasBack && "hidden"}">
|
|
<component-icon name="arrow_left"></component-icon>
|
|
${t("home")}
|
|
</a>
|
|
<div class="component_container">
|
|
<div class="error-page">
|
|
<h1>${t("Oops!")}</h1>
|
|
<h2>${t(safe(msg))}</h2>
|
|
<p>
|
|
<button class="light" data-bind="details">${t("More details")}</button>
|
|
<button class="primary" data-bind="refresh">${t("Reload")}</button>
|
|
<pre class="hidden"><code>${formatTrace(trace)}</code></pre>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`);
|
|
render($page);
|
|
|
|
// feature: show error details
|
|
effect(rxjs.fromEvent(qs($page, "button[data-bind=\"details\"]"), "click").pipe(
|
|
rxjs.mapTo(["hidden"]),
|
|
applyMutation(qs($page, "pre"), "classList", "toggle")
|
|
));
|
|
|
|
// feature: refresh button
|
|
const shouldHideRefreshButton = location.pathname === "/";
|
|
const $refresh = qs($page, "button[data-bind=\"refresh\"]");
|
|
if (shouldHideRefreshButton) $refresh.remove();
|
|
else effect(rxjs.fromEvent($refresh, "click").pipe(
|
|
rxjs.tap(() => shouldRedirectLogin ? navigate(link) : location.reload()),
|
|
));
|
|
|
|
return rxjs.EMPTY;
|
|
};
|
|
}
|
|
|
|
function processError(err) {
|
|
let msg, trace;
|
|
if (err instanceof AjaxError) {
|
|
msg = t(err.code());
|
|
trace = `
|
|
type: ${err.type()}
|
|
code: ${err.code()}
|
|
message: ${err.message}
|
|
trace: ${err.stack}`;
|
|
} else if (err instanceof ApplicationError) {
|
|
msg = t(err.message);
|
|
trace = `
|
|
type: ${err.type()}
|
|
debug: ${err.debug()}
|
|
trace: ${err.stack}`;
|
|
} else {
|
|
msg = t("Internal Error");
|
|
trace = `
|
|
type: Error
|
|
message: ${err.message}
|
|
trace: ${err.stack || "N/A"}`;
|
|
}
|
|
return [msg, trace.trim()];
|
|
}
|
|
|
|
function formatTrace(str) {
|
|
return str
|
|
.replaceAll("<", "<")
|
|
.replaceAll(">", ">")
|
|
.replaceAll(" ", " ")
|
|
.split("\n")
|
|
.map((line) => line.indexOf("/lib/vendor/") === -1 ? line : `<span style="opacity:0.25">${line}</span>`)
|
|
.join("\n");
|
|
}
|
|
|
|
const css = `
|
|
.error-page {
|
|
width: 80%;
|
|
max-width: 600px;
|
|
margin: 50px auto 0 auto;
|
|
flex-direction: column;
|
|
}
|
|
.error-page h1 {
|
|
margin: 5px 0;
|
|
font-size: 3.1em;
|
|
}
|
|
.error-page h2 {
|
|
margin: 10px 0;
|
|
font-weight: normal;
|
|
font-weight: 100;
|
|
}
|
|
.error-page button {
|
|
padding-left: 10px;
|
|
padding-right: 10px;
|
|
line-height: 1.2rem;
|
|
text-transform: capitalize;
|
|
}
|
|
.error-page code {
|
|
margin-top: 5px;
|
|
display: block;
|
|
padding: 10px;
|
|
overflow-x: auto;
|
|
overflow-y: auto;
|
|
background: #e2e2e2;
|
|
color: var(--dark);
|
|
border-radius: 3px;
|
|
max-height: 350px;
|
|
}
|
|
.error-page pre {
|
|
margin: 0;
|
|
}
|
|
.error-page p {
|
|
font-style: italic;
|
|
margin-bottom: 5px;
|
|
}
|
|
.error-page a {
|
|
border-bottom: 1px dashed;
|
|
}
|
|
|
|
.backnav {
|
|
display: inline-block;
|
|
padding: 10px 5px;
|
|
}
|
|
.backnav .component_icon {
|
|
height: 23px;
|
|
margin-right: -3px;
|
|
vertical-align: middle;
|
|
}
|
|
`;
|
|
|
|
function calculateBacklink(pathname = "") {
|
|
let url = "/";
|
|
const listPath = pathname.replace(new RegExp("/$"), "").split("/");
|
|
switch (listPath[1]) {
|
|
case "view": // in view mode, navigate to current folder
|
|
listPath[1] = "files";
|
|
listPath.pop();
|
|
url = listPath.join("/") + "/";
|
|
break;
|
|
case "files": // in file browser mode, navigate to parent folder
|
|
listPath.pop();
|
|
url = listPath.join("/") + "/";
|
|
break;
|
|
}
|
|
return toHref(url === "/files/" ? "/" : url);
|
|
}
|