mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 15:58:48 +00:00
Convert vault sync to async
This commit is contained in:
@@ -1101,18 +1101,18 @@ class PasswordManager:
|
|||||||
print(colored(f"Error: Failed to initialize managers: {e}", "red"))
|
print(colored(f"Error: Failed to initialize managers: {e}", "red"))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def sync_index_from_nostr(self) -> None:
|
async def sync_index_from_nostr_async(self) -> None:
|
||||||
"""Always fetch the latest vault data from Nostr and update the local index."""
|
"""Always fetch the latest vault data from Nostr and update the local index."""
|
||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
try:
|
try:
|
||||||
result = asyncio.run(self.nostr_client.fetch_latest_snapshot())
|
result = await self.nostr_client.fetch_latest_snapshot()
|
||||||
if not result:
|
if not result:
|
||||||
return
|
return
|
||||||
manifest, chunks = result
|
manifest, chunks = result
|
||||||
encrypted = gzip.decompress(b"".join(chunks))
|
encrypted = gzip.decompress(b"".join(chunks))
|
||||||
if manifest.delta_since:
|
if manifest.delta_since:
|
||||||
version = int(manifest.delta_since)
|
version = int(manifest.delta_since)
|
||||||
deltas = asyncio.run(self.nostr_client.fetch_deltas_since(version))
|
deltas = await self.nostr_client.fetch_deltas_since(version)
|
||||||
if deltas:
|
if deltas:
|
||||||
encrypted = deltas[-1]
|
encrypted = deltas[-1]
|
||||||
current = self.vault.get_encrypted_index()
|
current = self.vault.get_encrypted_index()
|
||||||
@@ -1128,18 +1128,19 @@ class PasswordManager:
|
|||||||
duration = time.perf_counter() - start
|
duration = time.perf_counter() - start
|
||||||
logger.info("sync_index_from_nostr completed in %.2f seconds", duration)
|
logger.info("sync_index_from_nostr completed in %.2f seconds", duration)
|
||||||
|
|
||||||
|
def sync_index_from_nostr(self) -> None:
|
||||||
|
asyncio.run(self.sync_index_from_nostr_async())
|
||||||
|
|
||||||
def start_background_sync(self) -> None:
|
def start_background_sync(self) -> None:
|
||||||
"""Launch a thread to synchronize the vault without blocking the UI."""
|
"""Launch a thread to synchronize the vault without blocking the UI."""
|
||||||
if getattr(self, "offline_mode", False):
|
if getattr(self, "offline_mode", False):
|
||||||
return
|
return
|
||||||
if (
|
if getattr(self, "_sync_task", None) and not getattr(
|
||||||
hasattr(self, "_sync_thread")
|
self._sync_task, "done", True
|
||||||
and self._sync_thread
|
|
||||||
and self._sync_thread.is_alive()
|
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
def _worker() -> None:
|
async def _worker() -> None:
|
||||||
try:
|
try:
|
||||||
if hasattr(self, "nostr_client") and hasattr(self, "vault"):
|
if hasattr(self, "nostr_client") and hasattr(self, "vault"):
|
||||||
self.attempt_initial_sync()
|
self.attempt_initial_sync()
|
||||||
@@ -1148,8 +1149,12 @@ class PasswordManager:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning(f"Background sync failed: {exc}")
|
logger.warning(f"Background sync failed: {exc}")
|
||||||
|
|
||||||
self._sync_thread = threading.Thread(target=_worker, daemon=True)
|
try:
|
||||||
self._sync_thread.start()
|
loop = asyncio.get_running_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
threading.Thread(target=lambda: asyncio.run(_worker()), daemon=True).start()
|
||||||
|
else:
|
||||||
|
self._sync_task = asyncio.create_task(_worker())
|
||||||
|
|
||||||
def start_background_relay_check(self) -> None:
|
def start_background_relay_check(self) -> None:
|
||||||
"""Check relay health in a background thread."""
|
"""Check relay health in a background thread."""
|
||||||
@@ -1185,13 +1190,18 @@ class PasswordManager:
|
|||||||
|
|
||||||
def _worker() -> None:
|
def _worker() -> None:
|
||||||
try:
|
try:
|
||||||
self.sync_vault(alt_summary=alt_summary)
|
asyncio.run(self.sync_vault_async(alt_summary=alt_summary))
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.error(f"Background vault sync failed: {exc}", exc_info=True)
|
logging.error(f"Background vault sync failed: {exc}", exc_info=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
except RuntimeError:
|
||||||
threading.Thread(target=_worker, daemon=True).start()
|
threading.Thread(target=_worker, daemon=True).start()
|
||||||
|
else:
|
||||||
|
asyncio.create_task(self.sync_vault_async(alt_summary=alt_summary))
|
||||||
|
|
||||||
def attempt_initial_sync(self) -> bool:
|
async def attempt_initial_sync_async(self) -> bool:
|
||||||
"""Attempt to download the initial vault snapshot from Nostr.
|
"""Attempt to download the initial vault snapshot from Nostr.
|
||||||
|
|
||||||
Returns ``True`` if the snapshot was successfully downloaded and the
|
Returns ``True`` if the snapshot was successfully downloaded and the
|
||||||
@@ -1205,13 +1215,13 @@ class PasswordManager:
|
|||||||
have_data = False
|
have_data = False
|
||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
try:
|
try:
|
||||||
result = asyncio.run(self.nostr_client.fetch_latest_snapshot())
|
result = await self.nostr_client.fetch_latest_snapshot()
|
||||||
if result:
|
if result:
|
||||||
manifest, chunks = result
|
manifest, chunks = result
|
||||||
encrypted = gzip.decompress(b"".join(chunks))
|
encrypted = gzip.decompress(b"".join(chunks))
|
||||||
if manifest.delta_since:
|
if manifest.delta_since:
|
||||||
version = int(manifest.delta_since)
|
version = int(manifest.delta_since)
|
||||||
deltas = asyncio.run(self.nostr_client.fetch_deltas_since(version))
|
deltas = await self.nostr_client.fetch_deltas_since(version)
|
||||||
if deltas:
|
if deltas:
|
||||||
encrypted = deltas[-1]
|
encrypted = deltas[-1]
|
||||||
success = self.vault.decrypt_and_save_index_from_nostr(
|
success = self.vault.decrypt_and_save_index_from_nostr(
|
||||||
@@ -1229,17 +1239,23 @@ class PasswordManager:
|
|||||||
|
|
||||||
return have_data
|
return have_data
|
||||||
|
|
||||||
|
def attempt_initial_sync(self) -> bool:
|
||||||
|
return asyncio.run(self.attempt_initial_sync_async())
|
||||||
|
|
||||||
def sync_index_from_nostr_if_missing(self) -> None:
|
def sync_index_from_nostr_if_missing(self) -> None:
|
||||||
"""Retrieve the password database from Nostr if it doesn't exist locally.
|
"""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
|
If no valid data is found or decryption fails, initialize a fresh local
|
||||||
database and publish it to Nostr.
|
database and publish it to Nostr.
|
||||||
"""
|
"""
|
||||||
success = self.attempt_initial_sync()
|
asyncio.run(self.sync_index_from_nostr_if_missing_async())
|
||||||
|
|
||||||
|
async def sync_index_from_nostr_if_missing_async(self) -> None:
|
||||||
|
success = await self.attempt_initial_sync_async()
|
||||||
if not success:
|
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()
|
await self.sync_vault_async()
|
||||||
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}")
|
||||||
|
|
||||||
@@ -3532,7 +3548,7 @@ class PasswordManager:
|
|||||||
# Re-raise the exception to inform the calling function of the failure
|
# Re-raise the exception to inform the calling function of the failure
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def sync_vault(
|
async def sync_vault_async(
|
||||||
self, alt_summary: str | None = None
|
self, alt_summary: str | None = None
|
||||||
) -> dict[str, list[str] | str] | None:
|
) -> dict[str, list[str] | str] | None:
|
||||||
"""Publish the current vault contents to Nostr and return event IDs."""
|
"""Publish the current vault contents to Nostr and return event IDs."""
|
||||||
@@ -3547,7 +3563,7 @@ class PasswordManager:
|
|||||||
event_id = None
|
event_id = None
|
||||||
if callable(pub_snap):
|
if callable(pub_snap):
|
||||||
if asyncio.iscoroutinefunction(pub_snap):
|
if asyncio.iscoroutinefunction(pub_snap):
|
||||||
manifest, event_id = asyncio.run(pub_snap(encrypted))
|
manifest, event_id = await pub_snap(encrypted)
|
||||||
else:
|
else:
|
||||||
manifest, event_id = pub_snap(encrypted)
|
manifest, event_id = pub_snap(encrypted)
|
||||||
else:
|
else:
|
||||||
@@ -3569,6 +3585,11 @@ class PasswordManager:
|
|||||||
logging.error(f"Failed to sync vault: {e}", exc_info=True)
|
logging.error(f"Failed to sync vault: {e}", exc_info=True)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def sync_vault(
|
||||||
|
self, alt_summary: str | None = None
|
||||||
|
) -> dict[str, list[str] | str] | None:
|
||||||
|
return asyncio.run(self.sync_vault_async(alt_summary=alt_summary))
|
||||||
|
|
||||||
def backup_database(self) -> None:
|
def backup_database(self) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a backup of the encrypted JSON index file.
|
Creates a backup of the encrypted JSON index file.
|
||||||
|
Reference in New Issue
Block a user