mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 07:48:57 +00:00
Merge pull request #188 from PR0M3TH3AN/codex/add-relay-list-auto-health-check
Add relay health check on startup
This commit is contained in:
@@ -11,6 +11,7 @@ logger = logging.getLogger(__name__)
|
|||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
MAX_RETRIES = 3 # Maximum number of retries for relay connections
|
MAX_RETRIES = 3 # Maximum number of retries for relay connections
|
||||||
RETRY_DELAY = 5 # Seconds to wait before retrying a failed connection
|
RETRY_DELAY = 5 # Seconds to wait before retrying a failed connection
|
||||||
|
MIN_HEALTHY_RELAYS = 2 # Minimum relays that should return data on startup
|
||||||
|
|
||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
# Application Directory and Paths
|
# Application Directory and Paths
|
||||||
|
@@ -8,6 +8,7 @@ from typing import List, Optional, Tuple
|
|||||||
import hashlib
|
import hashlib
|
||||||
import asyncio
|
import asyncio
|
||||||
import gzip
|
import gzip
|
||||||
|
import websockets
|
||||||
|
|
||||||
# Imports from the nostr-sdk library
|
# Imports from the nostr-sdk library
|
||||||
from nostr_sdk import (
|
from nostr_sdk import (
|
||||||
@@ -137,6 +138,42 @@ class NostrClient:
|
|||||||
await self.client.connect()
|
await self.client.connect()
|
||||||
logger.info(f"NostrClient connected to relays: {self.relays}")
|
logger.info(f"NostrClient connected to relays: {self.relays}")
|
||||||
|
|
||||||
|
async def _ping_relay(self, relay: str, timeout: float) -> bool:
|
||||||
|
"""Attempt to retrieve the latest event from a single relay."""
|
||||||
|
sub_id = "seedpass-health"
|
||||||
|
pubkey = self.keys.public_key().to_hex()
|
||||||
|
req = json.dumps(
|
||||||
|
["REQ", sub_id, {"kinds": [1], "authors": [pubkey], "limit": 1}]
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
async with websockets.connect(
|
||||||
|
relay, open_timeout=timeout, close_timeout=timeout
|
||||||
|
) as ws:
|
||||||
|
await ws.send(req)
|
||||||
|
while True:
|
||||||
|
msg = await asyncio.wait_for(ws.recv(), timeout=timeout)
|
||||||
|
data = json.loads(msg)
|
||||||
|
if data[0] == "EVENT":
|
||||||
|
return True
|
||||||
|
if data[0] == "EOSE":
|
||||||
|
return False
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def _check_relay_health(self, min_relays: int, timeout: float) -> int:
|
||||||
|
tasks = [self._ping_relay(r, timeout) for r in self.relays]
|
||||||
|
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
healthy = sum(1 for r in results if r is True)
|
||||||
|
if healthy < min_relays:
|
||||||
|
logger.warning(
|
||||||
|
"Only %s relays responded with data; consider adding more.", healthy
|
||||||
|
)
|
||||||
|
return healthy
|
||||||
|
|
||||||
|
def check_relay_health(self, min_relays: int = 2, timeout: float = 5.0) -> int:
|
||||||
|
"""Ping relays and return the count of those providing data."""
|
||||||
|
return asyncio.run(self._check_relay_health(min_relays, timeout))
|
||||||
|
|
||||||
def publish_json_to_nostr(
|
def publish_json_to_nostr(
|
||||||
self,
|
self,
|
||||||
encrypted_json: bytes,
|
encrypted_json: bytes,
|
||||||
|
@@ -40,6 +40,7 @@ from utils.password_prompt import (
|
|||||||
prompt_existing_password,
|
prompt_existing_password,
|
||||||
confirm_action,
|
confirm_action,
|
||||||
)
|
)
|
||||||
|
from constants import MIN_HEALTHY_RELAYS
|
||||||
|
|
||||||
from constants import (
|
from constants import (
|
||||||
APP_DIR,
|
APP_DIR,
|
||||||
@@ -763,6 +764,17 @@ class PasswordManager:
|
|||||||
parent_seed=getattr(self, "parent_seed", None),
|
parent_seed=getattr(self, "parent_seed", None),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if hasattr(self.nostr_client, "check_relay_health"):
|
||||||
|
healthy = self.nostr_client.check_relay_health(MIN_HEALTHY_RELAYS)
|
||||||
|
if healthy < MIN_HEALTHY_RELAYS:
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"Only {healthy} relay(s) responded with your latest event."
|
||||||
|
" Consider adding more relays via Settings.",
|
||||||
|
"yellow",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug("Managers re-initialized for the new fingerprint.")
|
logger.debug("Managers re-initialized for the new fingerprint.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@@ -75,3 +75,19 @@ def test_initialize_client_pool_add_relay_fallback(tmp_path):
|
|||||||
fc = client.client
|
fc = client.client
|
||||||
assert fc.added == client.relays
|
assert fc.added == client.relays
|
||||||
assert fc.connected is True
|
assert fc.connected is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_relay_health_runs_async(tmp_path, monkeypatch):
|
||||||
|
client = _setup_client(tmp_path, FakeAddRelayClient)
|
||||||
|
|
||||||
|
recorded = {}
|
||||||
|
|
||||||
|
async def fake_check(min_relays, timeout):
|
||||||
|
recorded["args"] = (min_relays, timeout)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
monkeypatch.setattr(client, "_check_relay_health", fake_check)
|
||||||
|
result = client.check_relay_health(3, timeout=2)
|
||||||
|
|
||||||
|
assert result == 1
|
||||||
|
assert recorded["args"] == (3, 2)
|
||||||
|
Reference in New Issue
Block a user