upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-02-26 13:31:44 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-02-26 15:26:18 +0000
commit0d6ed93e4d143bb066205543af13f0ec6ddbdd58 (patch)
tree1b1940460ec149e7e7e224d620ff1f8b9e0c55f3 /src/lib
parentee68ccadce6a6c90747cbdaae557babb4683413e (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.rs57
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
2151impl Display for FetchReport { 2208impl Display for FetchReport {