mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 15:58:48 +00:00
Improve Nostr backup restoration reliability
This commit is contained in:
@@ -583,6 +583,7 @@ class NostrClient:
|
|||||||
|
|
||||||
self.last_error = None
|
self.last_error = None
|
||||||
|
|
||||||
|
logger.debug("Searching for backup with current keys...")
|
||||||
try:
|
try:
|
||||||
primary_keys = Keys.parse(self.key_manager.keys.private_key_hex())
|
primary_keys = Keys.parse(self.key_manager.keys.private_key_hex())
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -592,6 +593,9 @@ class NostrClient:
|
|||||||
if result is not None:
|
if result is not None:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
logger.warning(
|
||||||
|
"No backup found with current keys. Falling back to legacy key derivation..."
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
legacy_keys = self.key_manager.generate_legacy_nostr_keys()
|
legacy_keys = self.key_manager.generate_legacy_nostr_keys()
|
||||||
legacy_sdk_keys = Keys.parse(legacy_keys.private_key_hex())
|
legacy_sdk_keys = Keys.parse(legacy_keys.private_key_hex())
|
||||||
@@ -601,10 +605,11 @@ class NostrClient:
|
|||||||
|
|
||||||
result = await self._fetch_manifest_with_keys(legacy_sdk_keys)
|
result = await self._fetch_manifest_with_keys(legacy_sdk_keys)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
|
logger.info("Found legacy backup with old key derivation.")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
if self.last_error is None:
|
if self.last_error is None:
|
||||||
self.last_error = "Snapshot not found on relays"
|
self.last_error = "No backup found on Nostr relays."
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@@ -25,6 +25,7 @@ import dataclasses
|
|||||||
from termcolor import colored
|
from termcolor import colored
|
||||||
from utils.color_scheme import color_text
|
from utils.color_scheme import color_text
|
||||||
from utils.input_utils import timed_input
|
from utils.input_utils import timed_input
|
||||||
|
from constants import MAX_RETRIES, RETRY_DELAY
|
||||||
|
|
||||||
from .encryption import EncryptionManager
|
from .encryption import EncryptionManager
|
||||||
from .entry_management import EntryManager
|
from .entry_management import EntryManager
|
||||||
@@ -1460,22 +1461,37 @@ class PasswordManager:
|
|||||||
except Exception as exc: # pragma: no cover - best effort
|
except Exception as exc: # pragma: no cover - best effort
|
||||||
logger.warning(f"Unable to publish fresh database: {exc}")
|
logger.warning(f"Unable to publish fresh database: {exc}")
|
||||||
|
|
||||||
def check_nostr_backup_exists(self, profile_id: str) -> bool:
|
def check_nostr_backup_exists(self, seed_phrase: str) -> bool:
|
||||||
"""Return ``True`` if a snapshot exists on Nostr for ``profile_id``."""
|
"""Return ``True`` if a snapshot exists on Nostr for ``seed_phrase``."""
|
||||||
if not self.nostr_client or getattr(self, "offline_mode", False):
|
if not self.nostr_client or getattr(self, "offline_mode", False):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
profile_id = calculate_profile_id(seed_phrase)
|
||||||
previous = self.nostr_client.fingerprint
|
previous = self.nostr_client.fingerprint
|
||||||
self.nostr_client.fingerprint = profile_id
|
|
||||||
try:
|
try:
|
||||||
|
if hasattr(self, "config_manager") and self.config_manager is not None:
|
||||||
|
cfg = self.config_manager.load_config(require_pin=False)
|
||||||
|
max_retries = int(cfg.get("nostr_max_retries", MAX_RETRIES))
|
||||||
|
delay = float(cfg.get("nostr_retry_delay", RETRY_DELAY))
|
||||||
|
else:
|
||||||
|
max_retries = MAX_RETRIES
|
||||||
|
delay = RETRY_DELAY
|
||||||
|
for attempt in range(max_retries):
|
||||||
|
self.nostr_client.fingerprint = profile_id
|
||||||
result = asyncio.run(self.nostr_client.fetch_latest_snapshot())
|
result = asyncio.run(self.nostr_client.fetch_latest_snapshot())
|
||||||
return result is not None
|
if result is not None:
|
||||||
|
return True
|
||||||
|
if attempt < max_retries - 1:
|
||||||
|
time.sleep(delay * (2**attempt))
|
||||||
|
return False
|
||||||
finally:
|
finally:
|
||||||
self.nostr_client.fingerprint = previous
|
self.nostr_client.fingerprint = previous
|
||||||
|
|
||||||
def restore_from_nostr_with_guidance(self, seed_phrase: str) -> None:
|
def restore_from_nostr_with_guidance(self, seed_phrase: str) -> None:
|
||||||
"""Restore a profile from Nostr, warning if no backup exists."""
|
"""Restore a profile from Nostr, warning if no backup exists."""
|
||||||
profile_id = calculate_profile_id(seed_phrase)
|
self.notify("Searching for Nostr backup, this may take a moment...")
|
||||||
have_backup = self.check_nostr_backup_exists(profile_id)
|
have_backup = self.check_nostr_backup_exists(seed_phrase)
|
||||||
|
|
||||||
if not have_backup:
|
if not have_backup:
|
||||||
print(colored("No Nostr backup found for this seed profile.", "yellow"))
|
print(colored("No Nostr backup found for this seed profile.", "yellow"))
|
||||||
if not confirm_action("Continue with an empty database? (Y/N): "):
|
if not confirm_action("Continue with an empty database? (Y/N): "):
|
||||||
@@ -1485,11 +1501,19 @@ class PasswordManager:
|
|||||||
if not fp:
|
if not fp:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if have_backup:
|
||||||
|
self.notify("Backup found. Restoring vault...")
|
||||||
success = self.attempt_initial_sync()
|
success = self.attempt_initial_sync()
|
||||||
if success:
|
if success:
|
||||||
|
self.notify("Vault restored successfully from Nostr.", level="INFO")
|
||||||
print(colored("Vault restored from Nostr.", "green"))
|
print(colored("Vault restored from Nostr.", "green"))
|
||||||
elif have_backup:
|
else:
|
||||||
|
self.notify(
|
||||||
|
"Failed to download or decrypt vault from Nostr.", level="ERROR"
|
||||||
|
)
|
||||||
print(colored("Failed to download vault from Nostr.", "red"))
|
print(colored("Failed to download vault from Nostr.", "red"))
|
||||||
|
else:
|
||||||
|
self.notify("Starting with a new, empty vault.", level="INFO")
|
||||||
|
|
||||||
def handle_add_password(self) -> None:
|
def handle_add_password(self) -> None:
|
||||||
try:
|
try:
|
||||||
|
Reference in New Issue
Block a user