pikvm/pikvm#1462: relative root location

This commit is contained in:
Maxim Devaev
2025-02-02 07:09:21 +02:00
parent b51ea5e374
commit 73238e18e9
36 changed files with 170 additions and 96 deletions

View File

@@ -23,6 +23,9 @@
"use strict";
import {ROOT_PREFIX} from "./vars.js";
export var browser = new function() {
// https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser/9851769
// https://github.com/fingerprintjs/fingerprintjs/discussions/641
@@ -133,12 +136,12 @@ export function checkBrowser(desktop_css, mobile_css) {
let force_desktop = (new URL(window.location.href)).searchParams.get("force_desktop");
let force_mobile = (new URL(window.location.href)).searchParams.get("force_mobile");
if ((force_desktop || !browser.is_mobile) && !force_mobile) {
__addCssLink("/share/css/x-desktop.css");
__addCssLink("x-desktop.css");
if (desktop_css) {
__addCssLink(desktop_css);
}
} else {
__addCssLink("/share/css/x-mobile.css");
__addCssLink("x-mobile.css");
if (mobile_css) {
__addCssLink(mobile_css);
}
@@ -148,6 +151,7 @@ export function checkBrowser(desktop_css, mobile_css) {
}
function __addCssLink(path) {
path = `${ROOT_PREFIX}share/css/${path}`;
console.log("===== Adding CSS:", path);
let el_head = document.getElementsByTagName("head")[0];
let el_link = document.createElement("link");

View File

@@ -23,6 +23,7 @@
"use strict";
import {ROOT_PREFIX} from "../vars.js";
import {tools, $} from "../tools.js";
import {checkBrowser} from "../bb.js";
import {wm, initWindowManager} from "../wm.js";
@@ -51,7 +52,7 @@ function __setAppText() {
}
function __loadKvmdInfo() {
tools.httpGet("/api/info", {"fields": "auth,meta,extras"}, function(http) {
tools.httpGet("api/info", {"fields": "auth,meta,extras"}, function(http) {
if (http.status === 200) {
let info = JSON.parse(http.responseText).result;
@@ -100,7 +101,7 @@ function __loadKvmdInfo() {
document.title = "PiKVM Index";
}
} else if (http.status === 401 || http.status === 403) {
document.location.href = "/login";
tools.currentOpen("login");
} else {
setTimeout(__loadKvmdInfo, 1000);
}
@@ -108,11 +109,14 @@ function __loadKvmdInfo() {
}
function __makeApp(id, path, icon, name) {
// Tailing slash in href is added to avoid Nginx 301 redirect
// when the location doesn't have tailing slash: "foo -> foo/".
// Reverse proxy over PiKVM can be misconfigured to handle this.
return `<li>
<div ${id ? "id=\"" + id + "\"" : ""} class="app">
<a href="${path}">
<a href="${ROOT_PREFIX}${path}/">
<div>
<img class="svg-gray" src="${icon}">
<img class="svg-gray" src="${ROOT_PREFIX}${icon}">
${tools.escape(name)}
</div>
</a>
@@ -121,9 +125,9 @@ function __makeApp(id, path, icon, name) {
}
function __logout() {
tools.httpPost("/api/auth/logout", null, function(http) {
tools.httpPost("api/auth/logout", null, function(http) {
if (http.status === 200 || http.status === 401 || http.status === 403) {
document.location.href = "/login";
tools.currentOpen("login");
} else {
wm.error("Logout error", http.responseText);
}

View File

@@ -31,7 +31,7 @@ export function main() {
}
function __loadKvmdInfo() {
tools.httpGet("/api/info", null, function(http) {
tools.httpGet("api/info", null, function(http) {
if (http.status === 200) {
let ipmi_port = JSON.parse(http.responseText).result.extras.ipmi.port;
let make_item = (comment, ipmi, api) => `
@@ -52,7 +52,7 @@ function __loadKvmdInfo() {
${make_item("Check the power status", "power status", "")}
`;
} else if (http.status === 401 || http.status === 403) {
document.location.href = "/login";
tools.currentOpen("login");
} else {
setTimeout(__loadKvmdInfo, 1000);
}

View File

@@ -94,7 +94,7 @@ export function Atx(__recorder) {
var __clickAtx = function(button) {
let click_button = function() {
tools.httpPost("/api/atx/click", {"button": button}, function(http) {
tools.httpPost("api/atx/click", {"button": button}, function(http) {
if (http.status === 409) {
wm.error("Performing another ATX operation for other client.<br>Please try again later.");
} else if (http.status !== 200) {

View File

@@ -23,6 +23,7 @@
"use strict";
import {ROOT_PREFIX} from "../vars.js";
import {tools, $, $$} from "../tools.js";
import {wm} from "../wm.js";
@@ -138,7 +139,7 @@ export function Gpio(__recorder) {
return `
<img
class="__gpio-led __gpio-led-${item.channel} inline-lamp-big led-gray"
src="/share/svg/led-circle.svg"
src="${ROOT_PREFIX}share/svg/led-circle.svg"
data-color="${item.color}"
/>
`;
@@ -202,7 +203,7 @@ export function Gpio(__recorder) {
confirm = el.getAttribute("data-confirm-off");
}
let act = () => {
__sendPost("/api/gpio/switch", {"channel": ch, "state": to});
__sendPost("api/gpio/switch", {"channel": ch, "state": to});
__recorder.recordGpioSwitchEvent(ch, to);
};
if (confirm) {
@@ -220,7 +221,7 @@ export function Gpio(__recorder) {
let ch = el.getAttribute("data-channel");
let confirm = el.getAttribute("data-confirm");
let act = () => {
__sendPost("/api/gpio/pulse", {"channel": ch});
__sendPost("api/gpio/pulse", {"channel": ch});
__recorder.recordGpioPulseEvent(ch);
};
if (confirm) {

View File

@@ -275,7 +275,7 @@ export function Hid(__getGeometry, __recorder) {
var __clickOutputsRadio = function(hid) {
let output = tools.radio.getValue(`hid-outputs-${hid}-radio`);
tools.httpPost("/api/hid/set_params", {[`${hid}_output`]: output}, function(http) {
tools.httpPost("api/hid/set_params", {[`${hid}_output`]: output}, function(http) {
if (http.status !== 200) {
wm.error("Can't configure HID", http.responseText);
}
@@ -284,7 +284,7 @@ export function Hid(__getGeometry, __recorder) {
var __clickJigglerSwitch = function() {
let enabled = $("hid-jiggler-switch").checked;
tools.httpPost("/api/hid/set_params", {"jiggler": enabled}, function(http) {
tools.httpPost("api/hid/set_params", {"jiggler": enabled}, function(http) {
if (http.status !== 200) {
wm.error(`Can't ${enabled ? "enabled" : "disable"} mouse jiggler`, http.responseText);
}
@@ -293,7 +293,7 @@ export function Hid(__getGeometry, __recorder) {
var __clickConnectSwitch = function() {
let connected = $("hid-connect-switch").checked;
tools.httpPost("/api/hid/set_connected", {"connected": connected}, function(http) {
tools.httpPost("api/hid/set_connected", {"connected": connected}, function(http) {
if (http.status !== 200) {
wm.error(`Can't ${connected ? "connect" : "disconnect"} HID`, http.responseText);
}
@@ -303,7 +303,7 @@ export function Hid(__getGeometry, __recorder) {
var __clickResetButton = function() {
wm.confirm("Are you sure you want to reset HID (keyboard & mouse)?").then(function(ok) {
if (ok) {
tools.httpPost("/api/hid/reset", null, function(http) {
tools.httpPost("api/hid/reset", null, function(http) {
if (http.status !== 200) {
wm.error("HID reset error", http.responseText);
}

View File

@@ -31,7 +31,7 @@ import {Session} from "./session.js";
export function main() {
if (checkBrowser(null, "/share/css/kvm/x-mobile.css")) {
if (checkBrowser(null, "kvm/x-mobile.css")) {
tools.storage.bindSimpleSwitch($("page-close-ask-switch"), "page.close.ask", true, function(value) {
if (value) {
window.onbeforeunload = function(event) {
@@ -48,7 +48,7 @@ export function main() {
initWindowManager();
tools.el.setOnClick($("open-log-button"), () => window.open("/api/log?seek=3600&follow=1", "_blank"));
tools.el.setOnClick($("open-log-button"), () => tools.windowOpen("api/log?seek=3600&follow=1"));
tools.storage.bindSimpleSwitch(
$("page-full-tab-stream-switch"),

View File

@@ -23,6 +23,7 @@
"use strict";
import {ROOT_PREFIX} from "../vars.js";
import {tools, $} from "../tools.js";
import {wm} from "../wm.js";
@@ -270,14 +271,14 @@ export function Msd() {
var __clickDownloadButton = function() {
let image = encodeURIComponent($("msd-image-selector").value);
window.open(`/api/msd/read?image=${image}`);
tools.windowOpen(`api/msd/read?image=${image}`);
};
var __clickRemoveButton = function() {
let name = $("msd-image-selector").value;
wm.confirm("Are you sure you want to remove this image?", name).then(function(ok) {
if (ok) {
tools.httpPost("/api/msd/remove", {"image": name}, function(http) {
tools.httpPost("api/msd/remove", {"image": name}, function(http) {
if (http.status !== 200) {
wm.error("Can't remove image", http.responseText);
}
@@ -287,7 +288,7 @@ export function Msd() {
};
var __sendParam = function(name, value) {
tools.httpPost("/api/msd/set_params", {[name]: value}, function(http) {
tools.httpPost("api/msd/set_params", {[name]: value}, function(http) {
if (http.status !== 200) {
wm.error("Can't configure Mass Storage", http.responseText);
}
@@ -301,10 +302,10 @@ export function Msd() {
let prefix = encodeURIComponent($("msd-new-part-selector").value);
if (file) {
let image = encodeURIComponent(file.name);
__http.open("POST", `/api/msd/write?prefix=${prefix}&image=${image}&remove_incomplete=1`, true);
__http.open("POST", `${ROOT_PREFIX}api/msd/write?prefix=${prefix}&image=${image}&remove_incomplete=1`, true);
} else {
let url = encodeURIComponent($("msd-new-url").value);
__http.open("POST", `/api/msd/write_remote?prefix=${prefix}&url=${url}&remove_incomplete=1`, true);
__http.open("POST", `${ROOT_PREFIX}api/msd/write_remote?prefix=${prefix}&url=${url}&remove_incomplete=1`, true);
}
__http.upload.timeout = 7 * 24 * 3600;
__http.onreadystatechange = __uploadStateChange;
@@ -360,7 +361,7 @@ export function Msd() {
};
var __clickConnectButton = function(connected) {
tools.httpPost("/api/msd/set_connected", {"connected": connected}, function(http) {
tools.httpPost("api/msd/set_connected", {"connected": connected}, function(http) {
if (http.status !== 200) {
wm.error("Can't switch Mass Storage", http.responseText);
}
@@ -373,7 +374,7 @@ export function Msd() {
var __clickResetButton = function() {
wm.confirm("Are you sure you want to reset Mass Storage?").then(function(ok) {
if (ok) {
tools.httpPost("/api/msd/reset", null, function(http) {
tools.httpPost("api/msd/reset", null, function(http) {
if (http.status !== 200) {
wm.error("Mass Storage reset error", http.responseText);
}

View File

@@ -186,7 +186,7 @@ export function Ocr(__getGeometry) {
"ocr_right": __sel.right,
"ocr_bottom": __sel.bottom,
};
tools.httpGet("/api/streamer/snapshot", params, function(http) {
tools.httpGet("api/streamer/snapshot", params, function(http) {
if (http.status === 200) {
wm.copyTextToClipboard(http.responseText);
} else {

View File

@@ -72,7 +72,7 @@ export function Paste(__recorder) {
tools.debug(`HID: paste-as-keys ${keymap}: ${text}`);
tools.httpPost("/api/hid/print", {"limit": 0, "keymap": keymap, "slow": slow}, function(http) {
tools.httpPost("api/hid/print", {"limit": 0, "keymap": keymap, "slow": slow}, function(http) {
tools.el.setEnabled($("hid-pak-text"), true);
tools.el.setEnabled($("hid-pak-button"), true);
tools.el.setEnabled($("hid-pak-keymap-selector"), true);

View File

@@ -293,7 +293,7 @@ export function Recorder() {
if (event.event.slow !== undefined) {
params["slow"] = event.event.slow;
}
tools.httpPost("/api/hid/print", params, function(http) {
tools.httpPost("api/hid/print", params, function(http) {
if (http.status === 413) {
wm.error("Too many text for paste!");
__stopProcess();
@@ -307,7 +307,7 @@ export function Recorder() {
return;
} else if (event.event_type === "atx_button") {
tools.httpPost("/api/atx/click", {"button": event.event.button}, function(http) {
tools.httpPost("api/atx/click", {"button": event.event.button}, function(http) {
if (http.status !== 200) {
wm.error("ATX error", http.responseText);
__stopProcess();
@@ -318,7 +318,7 @@ export function Recorder() {
return;
} else if (["gpio_switch", "gpio_pulse"].includes(event.event_type)) {
let path = "/api/gpio";
let path = "api/gpio";
let params = {"channel": event.event.channel};
if (event.event_type === "gpio_switch") {
path += "/switch";

View File

@@ -23,6 +23,7 @@
"use strict";
import {ROOT_PREFIX} from "../vars.js";
import {tools, $} from "../tools.js";
import {wm} from "../wm.js";
@@ -272,10 +273,15 @@ export function Session() {
let close_hook = null;
let has_webterm = (state.webterm && (state.webterm.enabled || state.webterm.started));
if (has_webterm) {
let path = "/" + state.webterm.path + "?disableLeaveAlert=true";
let loc = window.location;
let base = `${loc.protocol}//${loc.host}${loc.pathname}${ROOT_PREFIX}`;
// Tailing slash after state.webterm.path is added to avoid Nginx 301 redirect
// when the location doesn't have tailing slash: "foo -> foo/".
// Reverse proxy over PiKVM can be misconfigured to handle this.
let url = base + state.webterm.path + "/?disableLeaveAlert=true";
show_hook = function() {
tools.info("Terminal opened: ", path);
$("webterm-iframe").src = path;
tools.info("Terminal opened: ", url);
$("webterm-iframe").src = url;
};
close_hook = function() {
tools.info("Terminal closed");
@@ -291,9 +297,9 @@ export function Session() {
$("link-led").className = "led-yellow";
$("link-led").title = "Connecting...";
tools.httpGet("/api/auth/check", null, function(http) {
tools.httpGet("api/auth/check", null, function(http) {
if (http.status === 200) {
__ws = new WebSocket(`${tools.is_https ? "wss" : "ws"}://${location.host}/api/ws`);
__ws = new WebSocket(tools.makeWsUrl("api/ws"));
__ws.sendHidEvent = (event) => __sendHidEvent(__ws, event.event_type, event.event);
__ws.onopen = __wsOpenHandler;
__ws.onmessage = __wsMessageHandler;
@@ -302,7 +308,7 @@ export function Session() {
} else if (http.status === 401 || http.status === 403) {
window.onbeforeunload = () => null;
wm.error("Unexpected logout occured, please login again").then(function() {
document.location.href = "/login";
tools.currentOpen("login");
});
} else {
__wsCloseHandler(null);

View File

@@ -332,19 +332,14 @@ export function Streamer() {
};
var __clickScreenshotButton = function() {
let el = document.createElement("a");
el.href = "/api/streamer/snapshot";
el.target = "_blank";
document.body.appendChild(el);
el.click();
setTimeout(() => document.body.removeChild(el), 0);
tools.windowOpen("api/streamer/snapshot");
};
var __clickResetButton = function() {
wm.confirm("Are you sure you want to reset stream?").then(function(ok) {
if (ok) {
__resetStream();
tools.httpPost("/api/streamer/reset", null, function(http) {
tools.httpPost("api/streamer/reset", null, function(http) {
if (http.status !== 200) {
wm.error("Can't reset stream", http.responseText);
}
@@ -354,7 +349,7 @@ export function Streamer() {
};
var __sendParam = function(name, value) {
tools.httpPost("/api/streamer/set_params", {[name]: value}, function(http) {
tools.httpPost("api/streamer/set_params", {[name]: value}, function(http) {
if (http.status !== 200) {
wm.error("Can't configure stream", http.responseText);
}

View File

@@ -96,7 +96,7 @@ export function JanusStreamer(__setActive, __setInactive, __setInfo, __orient, _
__setInfo(false, false, "");
__logInfo("Starting Janus ...");
__janus = new _Janus({
"server": `${tools.is_https ? "wss" : "ws"}://${location.host}/janus/ws`,
"server": tools.makeWsUrl("janus/ws"),
"ipv6": true,
"destroyOnUnload": false,
"success": __attachJanus,

View File

@@ -79,7 +79,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo) {
__setInactive();
__setInfo(false, false, "");
__logInfo("Starting Media ...");
__ws = new WebSocket(`${tools.is_https ? "wss" : "ws"}://${location.host}/api/media/ws`);
__ws = new WebSocket(tools.makeWsUrl("api/media/ws"));
__ws.binaryType = "arraybuffer";
__ws.onopen = __wsOpenHandler;
__ws.onerror = __wsErrorHandler;

View File

@@ -23,6 +23,7 @@
"use strict";
import {ROOT_PREFIX} from "../vars.js";
import {tools, $} from "../tools.js";
@@ -72,7 +73,7 @@ export function MjpegStreamer(__setActive, __setInactive, __setInfo) {
self.stopStream = function() {
self.ensureStream(null);
let blank = "/share/png/blank-stream.png";
let blank = `${ROOT_PREFIX}share/png/blank-stream.png`;
if (!String.prototype.endsWith.call($("stream-image").src, blank)) {
$("stream-image").src = blank;
}
@@ -138,7 +139,7 @@ export function MjpegStreamer(__setActive, __setInactive, __setInfo) {
__setStreamInactive();
__stopChecking();
let path = `/streamer/stream?key=${__key}`;
let path = `${ROOT_PREFIX}streamer/stream?key=${__key}`;
if (tools.browser.is_safari || tools.browser.is_ios) {
// uStreamer fix for WebKit
__logInfo("Using dual_final_frames=1 to fix WebKit bugs");

View File

@@ -23,6 +23,7 @@
"use strict";
import {ROOT_PREFIX} from "../vars.js";
import {tools, $} from "../tools.js";
import {wm} from "../wm.js";
@@ -125,7 +126,7 @@ export function Switch() {
+ ":" + brightness.toString(16).padStart(2, "0")
+ ":" + color.blink_ms.toString(16).padStart(4, "0")
);
__sendPost("/api/switch/set_colors", {[role]: rgbx}, function() {
__sendPost("api/switch/set_colors", {[role]: rgbx}, function() {
el_color.value = (
"#"
+ color.red.toString(16).padStart(2, "0")
@@ -137,7 +138,7 @@ export function Switch() {
};
var __clickSetDefaultColorButton = function(role) {
__sendPost("/api/switch/set_colors", {[role]: "default"});
__sendPost("api/switch/set_colors", {[role]: "default"});
};
var __applyEdids = function(edids) {
@@ -210,7 +211,7 @@ export function Switch() {
if (ok) {
let name = $("__switch-edid-new-name-input").value;
let data = $("__switch-edid-new-data-text").value;
__sendPost("/api/switch/edids/create", {"name": name, "data": data});
__sendPost("api/switch/edids/create", {"name": name, "data": data});
}
});
};
@@ -222,7 +223,7 @@ export function Switch() {
let html = "Are you sure to remove this EDID?<br>Ports that used it will change it to the default.";
wm.confirm(html, name).then(function(ok) {
if (ok) {
__sendPost("/api/switch/edids/remove", {"id": edid_id});
__sendPost("api/switch/edids/remove", {"id": edid_id});
}
});
}
@@ -344,11 +345,11 @@ export function Switch() {
<td colspan=100>
<div class="buttons-row">
<button id="__switch-beacon-button-u${unit}" class="small" title="Toggle uplink Beacon Led">
<img id="__switch-beacon-led-u${unit}" class="inline-lamp led-gray" src="/share/svg/led-beacon.svg"/>
<img id="__switch-beacon-led-u${unit}" class="inline-lamp led-gray" src="${ROOT_PREFIX}share/svg/led-beacon.svg"/>
Uplink
</button>
<button id="__switch-beacon-button-d${unit}" class="small" title="Toggle downlink Beacon Led">
<img id="__switch-beacon-led-d${unit}" class="inline-lamp led-gray" src="/share/svg/led-beacon.svg"/>
<img id="__switch-beacon-led-d${unit}" class="inline-lamp led-gray" src="${ROOT_PREFIX}share/svg/led-beacon.svg"/>
Downlink
</button>
</div>
@@ -365,10 +366,10 @@ export function Switch() {
<td>
<div class="buttons-row">
<button id="__switch-port-button-p${port}" title="Activate this port">
<img id="__switch-port-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-circle.svg"/>
<img id="__switch-port-led-p${port}" class="inline-lamp led-gray" src="${ROOT_PREFIX}share/svg/led-circle.svg"/>
</button>
<button id="__switch-params-button-p${port}" title="Configure this port">
<img id="__switch-params-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-gear.svg"/>
<img id="__switch-params-led-p${port}" class="inline-lamp led-gray" src="${ROOT_PREFIX}share/svg/led-gear.svg"/>
</button>
</div>
</td>
@@ -385,14 +386,14 @@ export function Switch() {
</td>
<td style="font-size:1em">
<button id="__switch-beacon-button-p${port}" class="small" title="Toggle Beacon Led on this port">
<img id="__switch-beacon-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-beacon.svg"/>
<img id="__switch-beacon-led-p${port}" class="inline-lamp led-gray" src="${ROOT_PREFIX}share/svg/led-beacon.svg"/>
</button>
</td>
<td>
<img id="__switch-video-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-video.svg" title="Video Link"/>
<img id="__switch-usb-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-usb.svg" title="USB Link"/>
<img id="__switch-atx-power-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-atx-power.svg" title="Power Led"/>
<img id="__switch-atx-hdd-led-p${port}" class="inline-lamp led-gray" src="/share/svg/led-atx-hdd.svg" title="HDD Led"/>
<img id="__switch-video-led-p${port}" class="inline-lamp led-gray" src="${ROOT_PREFIX}share/svg/led-video.svg" title="Video Link"/>
<img id="__switch-usb-led-p${port}" class="inline-lamp led-gray" src="${ROOT_PREFIX}share/svg/led-usb.svg" title="USB Link"/>
<img id="__switch-atx-power-led-p${port}" class="inline-lamp led-gray" src="${ROOT_PREFIX}share/svg/led-atx-power.svg" title="Power Led"/>
<img id="__switch-atx-hdd-led-p${port}" class="inline-lamp led-gray" src="${ROOT_PREFIX}share/svg/led-atx-hdd.svg" title="HDD Led"/>
</td>
<td>
<div class="buttons-row">
@@ -524,7 +525,7 @@ export function Switch() {
for (let action of Object.keys(atx_actions)) {
params[`atx_click_${action}_delay`] = tools.slider.getValue($(`__switch-port-atx-click-${action}-delay-slider`));
};
__sendPost("/api/switch/set_port_params", params);
__sendPost("api/switch/set_port_params", params);
}
});
};
@@ -555,31 +556,31 @@ export function Switch() {
Otherwise, it will break a current USB operation (OS installation, Live CD, or whatever).
`);
} else {
__sendPost("/api/switch/set_active", {"port": port});
__sendPost("api/switch/set_active", {"port": port});
}
};
var __switchUplinkBeacon = function(unit) {
let state = false;
try { state = !__state.beacons.uplinks[unit]; } catch {}; // eslint-disable-line no-empty
__sendPost("/api/switch/set_beacon", {"uplink": unit, "state": state});
__sendPost("api/switch/set_beacon", {"uplink": unit, "state": state});
};
var __switchDownlinkBeacon = function(unit) {
let state = false;
try { state = !__state.beacons.downlinks[unit]; } catch {}; // eslint-disable-line no-empty
__sendPost("/api/switch/set_beacon", {"downlink": unit, "state": state});
__sendPost("api/switch/set_beacon", {"downlink": unit, "state": state});
};
var __switchPortBeacon = function(port) {
let state = false;
try { state = !__state.beacons.ports[port]; } catch {}; // eslint-disable-line no-empty
__sendPost("/api/switch/set_beacon", {"port": port, "state": state});
__sendPost("api/switch/set_beacon", {"port": port, "state": state});
};
var __atxClick = function(port, button) {
let click_button = function() {
__sendPost("/api/switch/atx/click", {"port": port, "button": button});
__sendPost("api/switch/atx/click", {"port": port, "button": button});
};
if ($("switch-atx-ask-switch").checked) {
wm.confirm(`

View File

@@ -51,9 +51,9 @@ function __login() {
} else {
let passwd = $("passwd-input").value + $("code-input").value;
let body = `user=${encodeURIComponent(user)}&passwd=${encodeURIComponent(passwd)}`;
tools.httpPost("/api/auth/login", null, function(http) {
tools.httpPost("api/auth/login", null, function(http) {
if (http.status === 200) {
document.location.href = "/";
tools.currentOpen("");
} else if (http.status === 403) {
wm.error("Invalid credentials").then(__tryAgain);
} else {

View File

@@ -23,6 +23,7 @@
"use strict";
import {ROOT_PREFIX} from "./vars.js";
import {browser} from "./bb.js";
@@ -39,7 +40,16 @@ export var tools = new function() {
/************************************************************************/
self.currentOpen = function(url) {
window.location.href = ROOT_PREFIX + url;
};
self.windowOpen = function(url) {
window.open(ROOT_PREFIX + url, "_blank");
};
self.httpRequest = function(method, url, params, callback, body=null, content_type=null, timeout=15000) {
url = ROOT_PREFIX + url;
if (params) {
params = new URLSearchParams(params);
if (params) {
@@ -68,6 +78,11 @@ export var tools = new function() {
self.httpRequest("POST", url, params, callback, body, content_type, timeout);
};
self.makeWsUrl = function(url) {
let proto = (self.is_https ? "wss://" : "ws://");
return proto + window.location.host + window.location.pathname + ROOT_PREFIX + url;
};
/************************************************************************/
self.escape = function(text) {
@@ -383,7 +398,7 @@ export var tools = new function() {
/************************************************************************/
self.is_https = (location.protocol === "https:");
self.is_https = (window.location.protocol === "https:");
self.cookies = new function() {
return {

31
web/share/js/vars.js Normal file
View File

@@ -0,0 +1,31 @@
/*****************************************************************************
# #
# KVMD - The main PiKVM daemon. #
# #
# Copyright (C) 2018-2024 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
"use strict";
export var ROOT_PREFIX = "./";
export function setRootPrefix(prefix) {
ROOT_PREFIX = prefix;
}

View File

@@ -31,7 +31,7 @@ export function main() {
}
function __loadKvmdInfo() {
tools.httpGet("/api/info", null, function(http) {
tools.httpGet("api/info", null, function(http) {
if (http.status === 200) {
let vnc_port = JSON.parse(http.responseText).result.extras.vnc.port;
$("vnc-text").innerHTML = `
@@ -39,7 +39,7 @@ function __loadKvmdInfo() {
$</span> vncviewer ${window.location.hostname}::${vnc_port}
`;
} else if (http.status === 401 || http.status === 403) {
document.location.href = "/login";
tools.currentOpen("login");
} else {
setTimeout(__loadKvmdInfo, 1000);
}