mirror of
https://github.com/nekename/OpenDeck.git
synced 2026-10-28 09:44:01 +00:00
feat: use alternative ports if default ports are already in use
This commit is contained in:
13 files changed
+71
-25
No files matched your search
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"identifier": "corsfetch",
|
||||
"local": true,
|
||||
"remote": { "urls": ["http://localhost:57118"] },
|
||||
"remote": { "urls": ["http://localhost:*"] },
|
||||
"windows": ["*"],
|
||||
"permissions": ["cors-fetch:default"]
|
||||
}
|
||||
@@ -56,6 +56,11 @@ pub async fn update_devices() {
|
||||
let _ = app.get_webview_window("main").unwrap().emit("devices", DEVICES.clone());
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn get_port_base() -> u16 {
|
||||
*crate::plugins::PORT_BASE
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn get_categories() -> HashMap<String, Category> {
|
||||
CATEGORIES.read().await.clone()
|
||||
|
||||
@@ -40,8 +40,9 @@ async fn main() {
|
||||
|
||||
let app = match Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
frontend::get_devices,
|
||||
frontend::restart,
|
||||
frontend::get_devices,
|
||||
frontend::get_port_base,
|
||||
frontend::get_categories,
|
||||
frontend::get_localisations,
|
||||
frontend::get_applications,
|
||||
|
||||
@@ -31,6 +31,20 @@ enum PluginInstance {
|
||||
pub static DEVICE_NAMESPACES: Lazy<RwLock<HashMap<String, String>>> = Lazy::new(|| RwLock::new(HashMap::new()));
|
||||
static INSTANCES: Lazy<Mutex<HashMap<String, PluginInstance>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
pub static PORT_BASE: Lazy<u16> = Lazy::new(|| {
|
||||
let mut base = 57116;
|
||||
loop {
|
||||
let websocket_result = std::net::TcpListener::bind(format!("0.0.0.0:{}", base));
|
||||
let webserver_result = std::net::TcpListener::bind(format!("0.0.0.0:{}", base + 2));
|
||||
if websocket_result.is_ok() && webserver_result.is_ok() {
|
||||
log::debug!("Using ports {} and {}", base, base + 2);
|
||||
break;
|
||||
}
|
||||
base += 1;
|
||||
}
|
||||
base
|
||||
});
|
||||
|
||||
/// Initialise a plugin from a given directory.
|
||||
pub async fn initialise_plugin(path: &path::Path) -> anyhow::Result<()> {
|
||||
let plugin_uuid = path.file_name().unwrap().to_str().unwrap();
|
||||
@@ -152,10 +166,11 @@ pub async fn initialise_plugin(path: &path::Path) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
let code_path = code_path.unwrap();
|
||||
let args = ["-port", "57116", "-pluginUUID", plugin_uuid, "-registerEvent", "registerPlugin", "-info"];
|
||||
let port_string = PORT_BASE.to_string();
|
||||
let args = ["-port", port_string.as_str(), "-pluginUUID", plugin_uuid, "-registerEvent", "registerPlugin", "-info"];
|
||||
|
||||
if code_path.to_lowercase().ends_with(".html") || code_path.to_lowercase().ends_with(".htm") || code_path.to_lowercase().ends_with(".xhtml") {
|
||||
let url = "http://localhost:57118/".to_owned() + path.join(code_path).to_str().unwrap();
|
||||
let url = format!("http://localhost:{}/", *PORT_BASE + 2) + path.join(code_path).to_str().unwrap();
|
||||
let window = tauri::WebviewWindowBuilder::new(APP_HANDLE.get().unwrap(), plugin_uuid.replace('.', "_"), tauri::WebviewUrl::External(url.parse()?))
|
||||
.title(plugin_uuid)
|
||||
.visible(false)
|
||||
@@ -180,7 +195,7 @@ pub async fn initialise_plugin(path: &path::Path) -> anyhow::Result<()> {
|
||||
}};
|
||||
opendeckInit();
|
||||
"#,
|
||||
port = 57116,
|
||||
port = *PORT_BASE,
|
||||
uuid = plugin_uuid,
|
||||
event = "registerPlugin",
|
||||
info = serde_json::to_string(&info)?
|
||||
@@ -431,7 +446,7 @@ pub fn initialise_plugins() {
|
||||
|
||||
/// Start the WebSocket server that plugins communicate with.
|
||||
async fn init_websocket_server() {
|
||||
let listener = match TcpListener::bind("0.0.0.0:57116").await {
|
||||
let listener = match TcpListener::bind(format!("0.0.0.0:{}", *PORT_BASE)).await {
|
||||
Ok(listener) => listener,
|
||||
Err(error) => {
|
||||
error!("Failed to bind plugin WebSocket server to socket: {}", error);
|
||||
|
||||
@@ -17,7 +17,7 @@ fn mime(extension: &str) -> String {
|
||||
/// Start a simple webserver to serve files of plugins that run in a browser environment.
|
||||
pub async fn init_webserver(prefix: PathBuf) {
|
||||
let server = {
|
||||
let listener = std::net::TcpListener::bind("0.0.0.0:57118").unwrap();
|
||||
let listener = std::net::TcpListener::bind(format!("0.0.0.0:{}", *super::PORT_BASE + 2)).unwrap();
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data:; frame-src http://localhost:57118; script-src 'self' 'unsafe-inline'; connect-src ipc: http://ipc.localhost https://openactionapi.github.io https://api.github.com https://raw.githubusercontent.com https://plugins.amankhanna.me;"
|
||||
"csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data:; frame-src http://localhost:*; script-src 'self' 'unsafe-inline'; connect-src ipc: http://ipc.localhost https://openactionapi.github.io https://api.github.com https://raw.githubusercontent.com https://plugins.amankhanna.me;"
|
||||
},
|
||||
"withGlobalTauri": true
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import MagnifyingGlass from "phosphor-svelte/lib/MagnifyingGlass";
|
||||
|
||||
import { getWebserverUrl } from "$lib/ports";
|
||||
import { localisations } from "$lib/settings";
|
||||
import { PRODUCT_NAME } from "$lib/singletons";
|
||||
|
||||
@@ -49,9 +50,7 @@
|
||||
<summary class="text-xl font-semibold dark:text-neutral-300">
|
||||
{#if icon || (actions[0] && plugins.find((x) => x.id == actions[0].plugin) && categories[name].actions.every((x) => x.plugin == actions[0].plugin))}
|
||||
<img
|
||||
src={icon
|
||||
? (!icon.startsWith("opendeck/") ? "http://localhost:57118/" + icon : icon.replace("opendeck", ""))
|
||||
: "http://localhost:57118/" + plugins.find((x) => x.id == actions[0].plugin).icon}
|
||||
src={icon ? (!icon.startsWith("opendeck/") ? getWebserverUrl(icon) : icon.replace("opendeck", "")) : getWebserverUrl(plugins.find((x) => x.id == actions[0].plugin).icon)}
|
||||
alt={name}
|
||||
class="w-5 h-5 rounded-xs ml-1 -mt-1 inline"
|
||||
/>
|
||||
@@ -67,7 +66,7 @@
|
||||
on:dragstart={(event) => event.dataTransfer?.setData("action", JSON.stringify(action))}
|
||||
>
|
||||
<img
|
||||
src={!action.icon.startsWith("opendeck/") ? "http://localhost:57118/" + action.icon : action.icon.replace("opendeck", "")}
|
||||
src={!action.icon.startsWith("opendeck/") ? getWebserverUrl(action.icon) : action.icon.replace("opendeck", "")}
|
||||
alt={$localisations?.[action.plugin]?.[action.uuid]?.Tooltip ?? action.tooltip}
|
||||
class="w-12 h-12 rounded-xs pointer-events-none"
|
||||
/>
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
import CloudArrowDown from "phosphor-svelte/lib/CloudArrowDown";
|
||||
import FileArrowUp from "phosphor-svelte/lib/FileArrowUp";
|
||||
import Gear from "phosphor-svelte/lib/Gear";
|
||||
import ListedPlugin from "./ListedPlugin.svelte";
|
||||
import MagnifyingGlass from "phosphor-svelte/lib/MagnifyingGlass";
|
||||
import Trash from "phosphor-svelte/lib/Trash";
|
||||
import WarningCircle from "phosphor-svelte/lib/WarningCircle";
|
||||
import ListedPlugin from "./ListedPlugin.svelte";
|
||||
import PluginDetails from "./PluginDetails.svelte";
|
||||
import Popup from "./Popup.svelte";
|
||||
import Tooltip from "./Tooltip.svelte";
|
||||
import Trash from "phosphor-svelte/lib/Trash";
|
||||
import WarningCircle from "phosphor-svelte/lib/WarningCircle";
|
||||
|
||||
import { getWebserverUrl } from "$lib/ports";
|
||||
import { localisations, settings } from "$lib/settings";
|
||||
import { actionList, deviceSelector } from "$lib/singletons";
|
||||
|
||||
@@ -168,7 +169,7 @@
|
||||
<div class="grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
{#each installed.sort((a, b) => (a.builtin && !b.builtin) ? -1 : (b.builtin && !a.builtin) ? 1 : a.id.localeCompare(b.id)) as plugin}
|
||||
<ListedPlugin
|
||||
icon="http://localhost:57118/{plugin.icon}"
|
||||
icon={getWebserverUrl(plugin.icon)}
|
||||
name={($localisations && $localisations[plugin.id] && $localisations[plugin.id].Name) ? $localisations[plugin.id].Name : plugin.name}
|
||||
subtitle={plugin.version}
|
||||
disconnected={!plugin.registered}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import type { DeviceInfo } from "$lib/DeviceInfo";
|
||||
import type { Profile } from "$lib/Profile";
|
||||
|
||||
import { getWebserverUrl, getWebSocketPort } from "$lib/ports";
|
||||
import { inspectedInstance } from "$lib/propertyInspector";
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
@@ -27,13 +28,13 @@
|
||||
coordinates = { row: Math.floor(position / device.columns), column: position % device.columns };
|
||||
}
|
||||
|
||||
if (instance == null || !iframe.src || !iframe.src.startsWith("http://localhost:57118")) return;
|
||||
if (instance == null || !iframe.src || !iframe.src.startsWith(getWebserverUrl())) return;
|
||||
const info = JSON.stringify(await invoke("make_info", { plugin: instance.action.plugin }));
|
||||
|
||||
iframe?.contentWindow?.postMessage({
|
||||
event: "connect",
|
||||
payload: [
|
||||
57116,
|
||||
getWebSocketPort(),
|
||||
instance.context,
|
||||
"registerPropertyInspector",
|
||||
info,
|
||||
@@ -50,7 +51,7 @@
|
||||
},
|
||||
}),
|
||||
],
|
||||
}, "http://localhost:57118");
|
||||
}, getWebserverUrl());
|
||||
}
|
||||
|
||||
const closePopup = (context: string) => {
|
||||
@@ -62,7 +63,7 @@
|
||||
iframe.style.width = "100%";
|
||||
iframe.style.height = "100%";
|
||||
iframe.style.display = $inspectedInstance == context ? "block" : "none";
|
||||
iframe.contentWindow?.postMessage({ event: "windowClosed" }, "http://localhost:57118");
|
||||
iframe.contentWindow?.postMessage({ event: "windowClosed" }, getWebserverUrl());
|
||||
}
|
||||
|
||||
iframePopupsOpen = iframePopupsOpen.filter((e) => e != context);
|
||||
@@ -140,9 +141,9 @@
|
||||
statusText: response.statusText,
|
||||
},
|
||||
},
|
||||
}, "http://localhost:57118");
|
||||
}, getWebserverUrl());
|
||||
}).catch((error: any) => {
|
||||
iframes[data.payload.context]?.contentWindow?.postMessage({ event: "fetchError", payload: { id: data.payload.id, error } }, "http://localhost:57118");
|
||||
iframes[data.payload.context]?.contentWindow?.postMessage({ event: "fetchError", payload: { id: data.payload.id, error } }, getWebserverUrl());
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -176,7 +177,7 @@
|
||||
title="Property inspector"
|
||||
class="w-full h-full hidden"
|
||||
class:block!={$inspectedInstance == instance.context}
|
||||
src={"http://localhost:57118/" + instance.action.property_inspector + "|opendeck_property_inspector"}
|
||||
src={getWebserverUrl(instance.action.property_inspector + "|opendeck_property_inspector")}
|
||||
name={instance.context}
|
||||
bind:this={iframes[instance.context]}
|
||||
on:load={() => iframeOnLoad(instance)}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
let portBase = 57116;
|
||||
let initialised = false;
|
||||
|
||||
export async function initPortBase(): Promise<void> {
|
||||
if (initialised) return;
|
||||
portBase = await invoke<number>("get_port_base");
|
||||
initialised = true;
|
||||
}
|
||||
|
||||
export function getWebSocketPort(): number {
|
||||
return portBase;
|
||||
}
|
||||
|
||||
export function getWebserverUrl(path: string = ""): string {
|
||||
return `http://localhost:${portBase + 2}/${path}`;
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
import type { ActionState } from "./ActionState.ts";
|
||||
import type { Context } from "./Context.ts";
|
||||
|
||||
import { getWebserverUrl } from "./ports.ts";
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export function getImage(image: string | undefined, fallback: string | undefined): string {
|
||||
if (!image) return fallback ? getImage(fallback, undefined) : "/alert.png";
|
||||
if (image.startsWith("opendeck/")) return image.replace("opendeck", "");
|
||||
if (!image.startsWith("data:")) return "http://localhost:57118/" + image;
|
||||
if (!image.startsWith("data:")) return getWebserverUrl(image);
|
||||
const svgxmlre = /^data:image\/svg\+xml(?!.*?;base64.*?)(?:;[\w=]*)*,(.+)/;
|
||||
const base64re = /^data:image\/(apng|avif|gif|jpeg|png|svg\+xml|webp|bmp|x-icon|tiff);base64,([A-Za-z0-9+/]+={0,2})?/;
|
||||
if (svgxmlre.test(image)) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type { DeviceInfo } from "$lib/DeviceInfo";
|
||||
import type { Profile } from "$lib/Profile";
|
||||
|
||||
import { initPortBase } from "$lib/ports";
|
||||
import { inspectedParentAction } from "$lib/propertyInspector";
|
||||
import { actionList, deviceSelector, profileManager } from "$lib/singletons";
|
||||
|
||||
@@ -18,6 +19,8 @@
|
||||
let devices: { [id: string]: DeviceInfo } = {};
|
||||
let selectedDevice: string;
|
||||
let selectedProfiles: { [id: string]: Profile } = {};
|
||||
|
||||
initPortBase();
|
||||
</script>
|
||||
|
||||
<svelte:window on:dragover={(event) => event.preventDefault()} on:drop={(event) => event.preventDefault()} />
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true
|
||||
"strict": true,
|
||||
"allowImportingTsExtensions": true
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user