Device IDs containing literal % characters (e.g., "202BC1-CODA%2D5519-000000") are double-encoded in URLs. Mithril 2.3.8 incorrectly double-decodes route parameters, corrupting device IDs with special characters. Workaround: Extract device ID directly from window.location.hash and decode once to get the correct value. See: https://github.com/MithrilJS/mithril.js/issues/3064
81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
import { ClosureComponent, Component } from "mithril";
|
|
import { m } from "./components.ts";
|
|
import config from "./config.ts";
|
|
import * as store from "./store.ts";
|
|
|
|
// Workaround for Mithril 2.3.8 regression
|
|
// https://github.com/MithrilJS/mithril.js/issues/3064
|
|
function getPathParamFromHash(hash: string, prefix: string): string | null {
|
|
if (!hash.startsWith(prefix)) return null;
|
|
|
|
const pathWithQuery = hash.slice(prefix.length);
|
|
|
|
// Stop at query string or hash fragment (mirrors Mithril's parsePathname)
|
|
const queryIdx = pathWithQuery.indexOf("?");
|
|
const hashIdx = pathWithQuery.indexOf("#");
|
|
let pathEnd = pathWithQuery.length;
|
|
if (queryIdx >= 0) pathEnd = Math.min(pathEnd, queryIdx);
|
|
if (hashIdx >= 0) pathEnd = Math.min(pathEnd, hashIdx);
|
|
|
|
const rawPath = pathWithQuery.slice(0, pathEnd);
|
|
|
|
// Normalize: collapse slashes, remove trailing slash (matches Mithril)
|
|
const normalizedPath = rawPath.replace(/\/{2,}/g, "/").replace(/\/$/, "");
|
|
|
|
// Decode once (Mithril already decoded with decodeURIComponentSafe)
|
|
return decodeURIComponent(normalizedPath);
|
|
}
|
|
|
|
export function init(
|
|
args: Record<string, unknown>,
|
|
): Promise<Record<string, unknown>> {
|
|
if (!window.authorizer.hasAccess("devices", 2)) {
|
|
return Promise.reject(
|
|
new Error("You are not authorized to view this page"),
|
|
);
|
|
}
|
|
|
|
const deviceId =
|
|
getPathParamFromHash(window.location.hash, "#!/devices/") ??
|
|
(args.id as string);
|
|
|
|
return Promise.resolve({
|
|
deviceId: deviceId,
|
|
deviceFilter: ["=", ["PARAM", "DeviceID.ID"], deviceId],
|
|
});
|
|
}
|
|
|
|
export const component: ClosureComponent = (): Component => {
|
|
return {
|
|
view: (vnode) => {
|
|
document.title = `${vnode.attrs["deviceId"]} - Devices - GenieACS`;
|
|
|
|
const dev = store.fetch("devices", vnode.attrs["deviceFilter"]);
|
|
if (!dev.value.length) {
|
|
if (!dev.fulfilling)
|
|
return m("p.error", `No such device ${vnode.attrs["deviceId"]}`);
|
|
return m(
|
|
"loading",
|
|
{ queries: [dev] },
|
|
m("div", { style: "height: 100px;" }),
|
|
);
|
|
}
|
|
|
|
const conf = config.ui.device;
|
|
const cmps = [];
|
|
|
|
for (const c of Object.values(conf)) {
|
|
cmps.push(
|
|
m.context(
|
|
{ device: dev.value[0], deviceQuery: dev },
|
|
store.evaluateExpression(c["type"], null) as string,
|
|
c,
|
|
),
|
|
);
|
|
}
|
|
|
|
return [m("h1", vnode.attrs["deviceId"]), cmps];
|
|
},
|
|
};
|
|
};
|