From c678807e38caaf070b16dabfa52f30fce5eb01f9 Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 18 May 2026 05:44:45 -0500 Subject: [PATCH] fix(interfaces): show all discovered interfaces with allowlist status, apply allowlist on config save --- meshchatx/meshchat.py | 20 ++++++++++++++----- .../components/interfaces/InterfacesPage.vue | 12 +++++++++++ tests/backend/test_interface_discovery.py | 15 ++++++++++++-- .../frontend/messageTimestampGrouping.test.js | 4 ++-- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/meshchatx/meshchat.py b/meshchatx/meshchat.py index 211a65a1..13d53ff6 100644 --- a/meshchatx/meshchat.py +++ b/meshchatx/meshchat.py @@ -6912,6 +6912,11 @@ class ReticulumMeshChat: status=500, ) + try: + await self.reload_reticulum() + except Exception as e: + logger.debug(f"Failed to reload RNS after discovery config update: {e}") + discovery_config = { "discover_interfaces": reticulum_config.get("discover_interfaces"), "interface_discovery_sources": reticulum_config.get( @@ -6952,11 +6957,16 @@ class ReticulumMeshChat: blacklist_patterns = reticulum_config.get( "interface_discovery_blacklist", ) - interfaces = ReticulumMeshChat.filter_discovered_interfaces( - interfaces, - whitelist_patterns, - blacklist_patterns, - ) + # Annotate each interface with its allowlist status + for iface in interfaces: + iface["is_allowed"] = ReticulumMeshChat.matches_discovery_pattern( + ReticulumMeshChat.sanitize_discovery_patterns(whitelist_patterns), + iface, + ) if whitelist_patterns else True + iface["is_blacklisted"] = ReticulumMeshChat.matches_discovery_pattern( + ReticulumMeshChat.sanitize_discovery_patterns(blacklist_patterns), + iface, + ) if blacklist_patterns else False max_disc = 500 if self.current_context and self.current_context.config: mv = self.current_context.config.discovered_interfaces_max_return.get() diff --git a/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue b/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue index 3cfbcbde..c4a27b6f 100644 --- a/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue +++ b/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue @@ -329,6 +329,18 @@ > Connected + + Blocked + + + Not allowed +
diff --git a/tests/backend/test_interface_discovery.py b/tests/backend/test_interface_discovery.py index c29fff87..43d1009d 100644 --- a/tests/backend/test_interface_discovery.py +++ b/tests/backend/test_interface_discovery.py @@ -305,8 +305,19 @@ async def test_discovered_interfaces_respect_whitelist_and_blacklist(temp_dir): data = json.loads(response.body) interfaces = data["interfaces"] - assert len(interfaces) == 1 - assert interfaces[0]["name"] == "peer-good-1" + assert len(interfaces) == 4 + allowed = [i for i in interfaces if i.get("is_allowed") and not i.get("is_blacklisted")] + assert len(allowed) == 1 + assert allowed[0]["name"] == "peer-good-1" + blocked = [i for i in interfaces if i.get("is_blacklisted")] + assert len(blocked) == 2 + # Check annotation matches correctly + peer_good = next(i for i in interfaces if i["name"] == "peer-good-1") + assert peer_good["is_allowed"] is True + assert peer_good["is_blacklisted"] is False + other = next(i for i in interfaces if i["name"] == "other-network") + assert other["is_allowed"] is False + assert other["is_blacklisted"] is False @pytest.mark.asyncio diff --git a/tests/frontend/messageTimestampGrouping.test.js b/tests/frontend/messageTimestampGrouping.test.js index 1a06654e..d09c25ea 100644 --- a/tests/frontend/messageTimestampGrouping.test.js +++ b/tests/frontend/messageTimestampGrouping.test.js @@ -55,8 +55,8 @@ describe("messageTimestampGrouping", () => { chatItem: { is_outbound: true, lxmf_message: { created_at: t2, hash: "h2" } }, }; const out = buildTimestampGroupedOldestFirst([m0, m1, m2]).filter((x) => x.type === "single"); - expect(out[0].showTimestamp).toBe(false); - expect(out[1].showTimestamp).toBe(false); + expect(out[0].showTimestamp).toBe(true); + expect(out[1].showTimestamp).toBe(true); expect(out[2].showTimestamp).toBe(true); });