diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-30 22:36:56 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-30 22:36:56 +0200 |
| commit | 2d6c276734fd19f80edf84cdb95f91a929df6365 (patch) | |
| tree | 41a697eb9c048218e0d800cbcb7fa39f4ede2b1d /packages/meshbay-node/src | |
| parent | 07e6e4271ea3a45e9cd364eb6ec05801a653d9e8 (diff) | |
| download | meshbay-2d6c276734fd19f80edf84cdb95f91a929df6365.tar.gz | |
feat(ui): configurable ICE interfaces in the Node page
The ice_interfaces setting (auto-exclude vs explicit whitelist) is now
editable from the Node page, persisted via the settings API and roster,
and hot-swapped at runtime by re-installing the aioice filter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/src')
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/daemon.py | 1 | ||||
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/ops.py | 17 | ||||
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/roster.py | 22 |
3 files changed, 26 insertions, 14 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/daemon.py b/packages/meshbay-node/src/meshbay_node/daemon.py index 51f78b7..5c6d86c 100644 --- a/packages/meshbay-node/src/meshbay_node/daemon.py +++ b/packages/meshbay-node/src/meshbay_node/daemon.py @@ -278,6 +278,7 @@ class NodeDaemon: "max_concurrent_streams": nd.max_concurrent_streams, "transcode_incompatible_video": nd.transcode_incompatible_video, "stun_servers": nd.stun_servers if nd.stun_servers else list(DEFAULT_STUN_SERVERS), + "ice_interfaces": nd.ice_interfaces, } effective = await self._roster.node_settings(defaults) for k, v in effective.items(): diff --git a/packages/meshbay-node/src/meshbay_node/ops.py b/packages/meshbay-node/src/meshbay_node/ops.py index 2ab6753..07579cd 100644 --- a/packages/meshbay-node/src/meshbay_node/ops.py +++ b/packages/meshbay-node/src/meshbay_node/ops.py @@ -341,6 +341,7 @@ async def list_groups(state: dict) -> dict: "max_concurrent_streams": nd.max_concurrent_streams if nd else 8, "transcode_incompatible_video": nd.transcode_incompatible_video if nd else True, "stun_servers": nd.stun_servers if nd and nd.stun_servers else list(DEFAULT_STUN_SERVERS), + "ice_interfaces": nd.ice_interfaces if nd else [], } if roster: settings = await roster.node_settings(defaults) @@ -799,6 +800,7 @@ async def get_node_settings(state: dict) -> dict: "max_concurrent_streams": nd.max_concurrent_streams, "transcode_incompatible_video": nd.transcode_incompatible_video, "stun_servers": nd.stun_servers if nd.stun_servers else list(DEFAULT_STUN_SERVERS), + "ice_interfaces": nd.ice_interfaces, } if roster: return await roster.node_settings(defaults) @@ -817,7 +819,8 @@ async def set_node_settings(state: dict, settings: dict) -> dict: "device_request_ttl_minutes": ("int", roster.SETTING_DEVICE_TTL), "max_concurrent_streams": ("int", roster.SETTING_MAX_STREAMS), "transcode_incompatible_video": ("bool", roster.SETTING_TRANSCODE), - "stun_servers": ("list", roster.SETTING_STUN_SERVERS), + "stun_servers": ("stun_list", roster.SETTING_STUN_SERVERS), + "ice_interfaces": ("list", roster.SETTING_ICE_INTERFACES), } set_by = state.get("node_user_id", "") @@ -841,14 +844,15 @@ async def set_node_settings(state: dict, settings: dict) -> dict: setattr(nd, key, v) await roster.set_node_setting(setting_key, "1" if v else "0", set_by) updated[key] = v - elif kind == "list": + elif kind in ("list", "stun_list"): import json as _json if not isinstance(value, list): raise OpError(f"{key} must be a list") v = [str(s) for s in value] - for s in v: - if not s.startswith("stun:"): - raise OpError(f"Invalid STUN server: {s} (must start with stun:)") + if kind == "stun_list": + for s in v: + if not s.startswith("stun:"): + raise OpError(f"Invalid STUN server: {s} (must start with stun:)") setattr(nd, key, v) await roster.set_node_setting(setting_key, _json.dumps(v), set_by) updated[key] = v @@ -863,6 +867,9 @@ async def set_node_settings(state: dict, settings: dict) -> dict: webrtc = state.get("webrtc") if webrtc and hasattr(webrtc, '_stun'): webrtc._stun = updated["stun_servers"] + if "ice_interfaces" in updated: + from meshbay_node.transport.ice_filter import install as install_ice_filter + install_ice_filter(updated["ice_interfaces"] or None) log.info("Node settings updated: %s", updated) return {"updated": updated} diff --git a/packages/meshbay-node/src/meshbay_node/roster.py b/packages/meshbay-node/src/meshbay_node/roster.py index 5bdb5ee..5af2b55 100644 --- a/packages/meshbay-node/src/meshbay_node/roster.py +++ b/packages/meshbay-node/src/meshbay_node/roster.py @@ -765,6 +765,7 @@ class Roster: SETTING_MAX_STREAMS = "max_concurrent_streams" SETTING_TRANSCODE = "transcode_incompatible_video" SETTING_STUN_SERVERS = "stun_servers" + SETTING_ICE_INTERFACES = "ice_interfaces" async def node_settings(self, defaults: dict) -> dict: """Current effective settings: roster override if present, else config default.""" @@ -785,15 +786,18 @@ class Roster: result[key] = int(stored) else: result[key] = defaults.get(key) - stored = await self.get_setting(self.NODE_WIDE_GROUP_ID, - self.SETTING_STUN_SERVERS) - if stored is not None: - try: - result["stun_servers"] = _json.loads(stored) - except (ValueError, TypeError): - result["stun_servers"] = defaults.get("stun_servers", []) - else: - result["stun_servers"] = defaults.get("stun_servers", []) + for list_key, setting in [ + ("stun_servers", self.SETTING_STUN_SERVERS), + ("ice_interfaces", self.SETTING_ICE_INTERFACES), + ]: + stored = await self.get_setting(self.NODE_WIDE_GROUP_ID, setting) + if stored is not None: + try: + result[list_key] = _json.loads(stored) + except (ValueError, TypeError): + result[list_key] = defaults.get(list_key, []) + else: + result[list_key] = defaults.get(list_key, []) return result async def set_node_setting(self, key: str, value: str, |