Return all Nostr event IDs

This commit is contained in:
thePR0M3TH3AN
2025-07-17 15:21:08 -04:00
parent f3c223a9a1
commit c787651899
6 changed files with 54 additions and 19 deletions

View File

@@ -365,14 +365,15 @@ def handle_post_to_nostr(
Handles the action of posting the encrypted password index to Nostr. Handles the action of posting the encrypted password index to Nostr.
""" """
try: try:
event_id = password_manager.sync_vault(alt_summary=alt_summary) result = password_manager.sync_vault(alt_summary=alt_summary)
if event_id: if result:
print( print(colored("\N{WHITE HEAVY CHECK MARK} Sync complete.", "green"))
colored( print("Event IDs:")
f"\N{WHITE HEAVY CHECK MARK} Sync complete. Event ID: {event_id}", print(f" manifest: {result['manifest_id']}")
"green", for cid in result["chunk_ids"]:
) print(f" chunk: {cid}")
) for did in result["delta_ids"]:
print(f" delta: {did}")
logging.info("Encrypted index posted to Nostr successfully.") logging.info("Encrypted index posted to Nostr successfully.")
else: else:
print(colored("\N{CROSS MARK} Sync failed…", "red")) print(colored("\N{CROSS MARK} Sync failed…", "red"))

View File

@@ -3517,8 +3517,10 @@ 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(self, alt_summary: str | None = None) -> str | None: def sync_vault(
"""Publish the current vault contents to Nostr.""" self, alt_summary: str | None = None
) -> dict[str, list[str] | str] | None:
"""Publish the current vault contents to Nostr and return event IDs."""
try: try:
if getattr(self, "offline_mode", False): if getattr(self, "offline_mode", False):
return None return None
@@ -3526,16 +3528,28 @@ class PasswordManager:
if not encrypted: if not encrypted:
return None return None
pub_snap = getattr(self.nostr_client, "publish_snapshot", None) pub_snap = getattr(self.nostr_client, "publish_snapshot", None)
manifest = None
event_id = None
if callable(pub_snap): if callable(pub_snap):
if asyncio.iscoroutinefunction(pub_snap): if asyncio.iscoroutinefunction(pub_snap):
_, event_id = asyncio.run(pub_snap(encrypted)) manifest, event_id = asyncio.run(pub_snap(encrypted))
else: else:
_, event_id = pub_snap(encrypted) manifest, event_id = pub_snap(encrypted)
else: else:
# Fallback for tests using simplified stubs # Fallback for tests using simplified stubs
event_id = self.nostr_client.publish_json_to_nostr(encrypted) event_id = self.nostr_client.publish_json_to_nostr(encrypted)
self.is_dirty = False self.is_dirty = False
return event_id if event_id is None:
return None
chunk_ids: list[str] = []
if manifest is not None:
chunk_ids = [c.event_id for c in manifest.chunks if c.event_id]
delta_ids = getattr(self.nostr_client, "_delta_events", [])
return {
"manifest_id": event_id,
"chunk_ids": chunk_ids,
"delta_ids": list(delta_ids),
}
except Exception as e: except Exception as e:
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

View File

@@ -419,9 +419,14 @@ def vault_reveal_parent_seed(
def nostr_sync(ctx: typer.Context) -> None: def nostr_sync(ctx: typer.Context) -> None:
"""Sync with configured Nostr relays.""" """Sync with configured Nostr relays."""
pm = _get_pm(ctx) pm = _get_pm(ctx)
event_id = pm.sync_vault() result = pm.sync_vault()
if event_id: if result:
typer.echo(event_id) typer.echo("Event IDs:")
typer.echo(f"- manifest: {result['manifest_id']}")
for cid in result["chunk_ids"]:
typer.echo(f"- chunk: {cid}")
for did in result["delta_ids"]:
typer.echo(f"- delta: {did}")
else: else:
typer.echo("Error: Failed to sync vault") typer.echo("Error: Failed to sync vault")

View File

@@ -53,7 +53,11 @@ class DummyPM:
self.nostr_client = SimpleNamespace( self.nostr_client = SimpleNamespace(
key_manager=SimpleNamespace(get_npub=lambda: "npub") key_manager=SimpleNamespace(get_npub=lambda: "npub")
) )
self.sync_vault = lambda: "event" self.sync_vault = lambda: {
"manifest_id": "event",
"chunk_ids": ["c1"],
"delta_ids": [],
}
self.config_manager = SimpleNamespace( self.config_manager = SimpleNamespace(
load_config=lambda require_pin=False: {"inactivity_timeout": 30}, load_config=lambda require_pin=False: {"inactivity_timeout": 30},
set_inactivity_timeout=lambda v: None, set_inactivity_timeout=lambda v: None,

View File

@@ -9,12 +9,17 @@ import main
def test_handle_post_success(capsys): def test_handle_post_success(capsys):
pm = SimpleNamespace( pm = SimpleNamespace(
sync_vault=lambda alt_summary=None: "abcd", sync_vault=lambda alt_summary=None: {
"manifest_id": "abcd",
"chunk_ids": ["c1", "c2"],
"delta_ids": ["d1"],
},
) )
main.handle_post_to_nostr(pm) main.handle_post_to_nostr(pm)
out = capsys.readouterr().out out = capsys.readouterr().out
assert "✅ Sync complete." in out assert "✅ Sync complete." in out
assert "abcd" in out assert "abcd" in out
assert "c1" in out and "c2" in out and "d1" in out
def test_handle_post_failure(capsys): def test_handle_post_failure(capsys):

View File

@@ -288,7 +288,11 @@ def test_nostr_sync(monkeypatch):
def sync_vault(): def sync_vault():
called["called"] = True called["called"] = True
return "evt123" return {
"manifest_id": "evt123",
"chunk_ids": ["c1"],
"delta_ids": ["d1"],
}
pm = SimpleNamespace(sync_vault=sync_vault, select_fingerprint=lambda fp: None) pm = SimpleNamespace(sync_vault=sync_vault, select_fingerprint=lambda fp: None)
monkeypatch.setattr(cli, "PasswordManager", lambda: pm) monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
@@ -296,6 +300,8 @@ def test_nostr_sync(monkeypatch):
assert result.exit_code == 0 assert result.exit_code == 0
assert called.get("called") is True assert called.get("called") is True
assert "evt123" in result.stdout assert "evt123" in result.stdout
assert "c1" in result.stdout
assert "d1" in result.stdout
def test_generate_password(monkeypatch): def test_generate_password(monkeypatch):