diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-26 13:31:44 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-26 15:26:18 +0000 |
| commit | 0d6ed93e4d143bb066205543af13f0ec6ddbdd58 (patch) | |
| tree | 1b1940460ec149e7e7e224d620ff1f8b9e0c55f3 /src/lib | |
| parent | ee68ccadce6a6c90747cbdaae557babb4683413e (diff) | |
feat: publish state event to stale grasp relays before sync push
FetchReport now captures the full state event seen on each relay during
the nostr fetch (state_per_relay: HashMap<RelayUrl, Option<Event>>).
ngit sync uses this to identify grasp server relays with a missing or
outdated state event and publishes the current state event to them
before attempting git pushes, preventing rejections. An existing login
is loaded silently (no prompt, no profile fetch) to provide a signer
for NIP-42 auth if requested.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/client.rs | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/src/lib/client.rs b/src/lib/client.rs index 3f1f22e..8ae9820 100644 --- a/src/lib/client.rs +++ b/src/lib/client.rs | |||
| @@ -840,6 +840,33 @@ impl Connect for Client { | |||
| 840 | let events: Vec<nostr::Event> = get_events_of(&relay, filters.clone(), pb).await?; | 840 | let events: Vec<nostr::Event> = get_events_of(&relay, filters.clone(), pb).await?; |
| 841 | // TODO: try reconcile | 841 | // TODO: try reconcile |
| 842 | 842 | ||
| 843 | // Track the best state event seen from this relay so callers can | ||
| 844 | // determine which relays have a stale or absent state event. | ||
| 845 | // We must do this before process_fetched_events because the local | ||
| 846 | // database only stores the canonical latest event; per-relay | ||
| 847 | // visibility is only available here. | ||
| 848 | for event in &events { | ||
| 849 | if event.kind.eq(&STATE_KIND) { | ||
| 850 | let entry = report | ||
| 851 | .state_per_relay | ||
| 852 | .entry(relay_url.clone()) | ||
| 853 | .or_insert(None); | ||
| 854 | let is_newer = entry.as_ref().is_none_or(|existing: &nostr::Event| { | ||
| 855 | event.created_at.gt(&existing.created_at) | ||
| 856 | || (event.created_at.eq(&existing.created_at) | ||
| 857 | && event.id.gt(&existing.id)) | ||
| 858 | }); | ||
| 859 | if is_newer { | ||
| 860 | *entry = Some(event.clone()); | ||
| 861 | } | ||
| 862 | } | ||
| 863 | } | ||
| 864 | // Mark relay as queried even if no state event was returned. | ||
| 865 | report | ||
| 866 | .state_per_relay | ||
| 867 | .entry(relay_url.clone()) | ||
| 868 | .or_insert(None); | ||
| 869 | |||
| 843 | process_fetched_events( | 870 | process_fetched_events( |
| 844 | events, | 871 | events, |
| 845 | &request, | 872 | &request, |
| @@ -2018,6 +2045,30 @@ pub fn consolidate_fetch_reports(reports: Vec<Result<FetchReport>>) -> FetchRepo | |||
| 2018 | for c in relay_report.profile_updates { | 2045 | for c in relay_report.profile_updates { |
| 2019 | report.profile_updates.insert(c); | 2046 | report.profile_updates.insert(c); |
| 2020 | } | 2047 | } |
| 2048 | // Per-relay state events are independent: each relay entry is kept as-is. | ||
| 2049 | // If a relay appears in multiple per-relay reports (shouldn't happen in | ||
| 2050 | // practice but possible in tests), keep the newer event. | ||
| 2051 | for (relay_url, maybe_event) in relay_report.state_per_relay { | ||
| 2052 | match report.state_per_relay.entry(relay_url) { | ||
| 2053 | std::collections::hash_map::Entry::Vacant(e) => { | ||
| 2054 | e.insert(maybe_event); | ||
| 2055 | } | ||
| 2056 | std::collections::hash_map::Entry::Occupied(mut e) => { | ||
| 2057 | let keep = match (e.get(), &maybe_event) { | ||
| 2058 | (None, Some(_)) => true, | ||
| 2059 | (Some(existing), Some(incoming)) => { | ||
| 2060 | incoming.created_at.gt(&existing.created_at) | ||
| 2061 | || (incoming.created_at.eq(&existing.created_at) | ||
| 2062 | && incoming.id.gt(&existing.id)) | ||
| 2063 | } | ||
| 2064 | _ => false, | ||
| 2065 | }; | ||
| 2066 | if keep { | ||
| 2067 | e.insert(maybe_event); | ||
| 2068 | } | ||
| 2069 | } | ||
| 2070 | } | ||
| 2071 | } | ||
| 2021 | } | 2072 | } |
| 2022 | report | 2073 | report |
| 2023 | } | 2074 | } |
| @@ -2146,6 +2197,12 @@ pub struct FetchReport { | |||
| 2146 | statuses: HashSet<EventId>, | 2197 | statuses: HashSet<EventId>, |
| 2147 | contributor_profiles: HashSet<PublicKey>, | 2198 | contributor_profiles: HashSet<PublicKey>, |
| 2148 | profile_updates: HashSet<PublicKey>, | 2199 | profile_updates: HashSet<PublicKey>, |
| 2200 | /// The best (newest) state event seen on each relay during the fetch. | ||
| 2201 | /// `None` as a value means the relay was queried but returned no state | ||
| 2202 | /// event at all. Relays that were never queried are absent from the map. | ||
| 2203 | /// This is the only point at which per-relay state visibility is available; | ||
| 2204 | /// the local database only stores the canonical latest event. | ||
| 2205 | pub state_per_relay: HashMap<RelayUrl, Option<nostr::Event>>, | ||
| 2149 | } | 2206 | } |
| 2150 | 2207 | ||
| 2151 | impl Display for FetchReport { | 2208 | impl Display for FetchReport { |