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

@@ -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(`