mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 07:48:57 +00:00
Merge pull request #607 from PR0M3TH3AN/codex/implement-initial-sync-in-passwordmanager
Introduce attempt_initial_sync
This commit is contained in:
@@ -1127,7 +1127,7 @@ class PasswordManager:
|
|||||||
def _worker() -> None:
|
def _worker() -> None:
|
||||||
try:
|
try:
|
||||||
if hasattr(self, "nostr_client") and hasattr(self, "vault"):
|
if hasattr(self, "nostr_client") and hasattr(self, "vault"):
|
||||||
self.sync_index_from_nostr_if_missing()
|
self.attempt_initial_sync()
|
||||||
if hasattr(self, "sync_index_from_nostr"):
|
if hasattr(self, "sync_index_from_nostr"):
|
||||||
self.sync_index_from_nostr()
|
self.sync_index_from_nostr()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -1176,16 +1176,19 @@ class PasswordManager:
|
|||||||
|
|
||||||
threading.Thread(target=_worker, daemon=True).start()
|
threading.Thread(target=_worker, daemon=True).start()
|
||||||
|
|
||||||
def sync_index_from_nostr_if_missing(self) -> None:
|
def attempt_initial_sync(self) -> bool:
|
||||||
"""Retrieve the password database from Nostr if it doesn't exist locally.
|
"""Attempt to download the initial vault snapshot from Nostr.
|
||||||
|
|
||||||
If no valid data is found or decryption fails, initialize a fresh local
|
Returns ``True`` if the snapshot was successfully downloaded and the
|
||||||
database and publish it to Nostr.
|
local index file was written. Returns ``False`` otherwise. The local
|
||||||
|
index file is not created on failure.
|
||||||
"""
|
"""
|
||||||
index_file = self.fingerprint_dir / "seedpass_entries_db.json.enc"
|
index_file = self.fingerprint_dir / "seedpass_entries_db.json.enc"
|
||||||
if index_file.exists():
|
if index_file.exists():
|
||||||
return
|
return True
|
||||||
|
|
||||||
have_data = False
|
have_data = False
|
||||||
|
start = time.perf_counter()
|
||||||
try:
|
try:
|
||||||
result = asyncio.run(self.nostr_client.fetch_latest_snapshot())
|
result = asyncio.run(self.nostr_client.fetch_latest_snapshot())
|
||||||
if result:
|
if result:
|
||||||
@@ -1202,10 +1205,23 @@ class PasswordManager:
|
|||||||
if success:
|
if success:
|
||||||
logger.info("Initialized local database from Nostr.")
|
logger.info("Initialized local database from Nostr.")
|
||||||
have_data = True
|
have_data = True
|
||||||
except Exception as e:
|
except Exception as e: # pragma: no cover - network errors
|
||||||
logger.warning(f"Unable to sync index from Nostr: {e}")
|
logger.warning(f"Unable to sync index from Nostr: {e}")
|
||||||
|
finally:
|
||||||
|
if getattr(self, "verbose_timing", False):
|
||||||
|
duration = time.perf_counter() - start
|
||||||
|
logger.info("attempt_initial_sync completed in %.2f seconds", duration)
|
||||||
|
|
||||||
if not have_data:
|
return have_data
|
||||||
|
|
||||||
|
def sync_index_from_nostr_if_missing(self) -> None:
|
||||||
|
"""Retrieve the password database from Nostr if it doesn't exist locally.
|
||||||
|
|
||||||
|
If no valid data is found or decryption fails, initialize a fresh local
|
||||||
|
database and publish it to Nostr.
|
||||||
|
"""
|
||||||
|
success = self.attempt_initial_sync()
|
||||||
|
if not success:
|
||||||
self.vault.save_index({"schema_version": LATEST_VERSION, "entries": {}})
|
self.vault.save_index({"schema_version": LATEST_VERSION, "entries": {}})
|
||||||
try:
|
try:
|
||||||
self.sync_vault()
|
self.sync_vault()
|
||||||
|
@@ -47,7 +47,8 @@ def test_full_sync_roundtrip(dummy_nostr_client):
|
|||||||
manifest_id = relay.manifests[-1].id
|
manifest_id = relay.manifests[-1].id
|
||||||
|
|
||||||
# Manager B retrieves snapshot
|
# Manager B retrieves snapshot
|
||||||
pm_b.sync_index_from_nostr_if_missing()
|
result = pm_b.attempt_initial_sync()
|
||||||
|
assert result is True
|
||||||
entries = pm_b.entry_manager.list_entries()
|
entries = pm_b.entry_manager.list_entries()
|
||||||
assert [e[1] for e in entries] == ["site1"]
|
assert [e[1] for e in entries] == ["site1"]
|
||||||
|
|
||||||
|
@@ -47,7 +47,8 @@ def test_full_sync_roundtrip(dummy_nostr_client):
|
|||||||
manifest_id = relay.manifests[-1].id
|
manifest_id = relay.manifests[-1].id
|
||||||
|
|
||||||
# Manager B retrieves snapshot
|
# Manager B retrieves snapshot
|
||||||
pm_b.sync_index_from_nostr_if_missing()
|
result = pm_b.attempt_initial_sync()
|
||||||
|
assert result is True
|
||||||
entries = pm_b.entry_manager.list_entries()
|
entries = pm_b.entry_manager.list_entries()
|
||||||
assert [e[1] for e in entries] == ["site1"]
|
assert [e[1] for e in entries] == ["site1"]
|
||||||
|
|
||||||
|
@@ -81,6 +81,7 @@ def test_sync_index_missing_bad_data(monkeypatch, dummy_nostr_client):
|
|||||||
)
|
)
|
||||||
monkeypatch.setattr(client, "fetch_deltas_since", lambda *_a, **_k: [])
|
monkeypatch.setattr(client, "fetch_deltas_since", lambda *_a, **_k: [])
|
||||||
|
|
||||||
pm.sync_index_from_nostr_if_missing()
|
result = pm.attempt_initial_sync()
|
||||||
data = pm.vault.load_index()
|
assert result is False
|
||||||
assert data["entries"] == {}
|
index_path = dir_path / "seedpass_entries_db.json.enc"
|
||||||
|
assert not index_path.exists()
|
||||||
|
Reference in New Issue
Block a user