upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/lib/login
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-08-06 12:52:59 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2025-08-07 17:25:50 +0100
commita9b2ebf8216be34950e54dd9a446dbdc0c9c744a (patch)
tree5a103933852fbcfcd42b13716cb92eeca5325d6d /src/lib/login
parent29f61ffdf155ea88b8d9aec23d28cf70baba577e (diff)
feat(send): PR fallback to user / custom grasp
if use is maintainer, push PR to all repo git servers. if user has a fork, push to all git servers it lists, and repo grasp servers. if user hasn't got a fork but has a user grasp list and pushing push to repo grasp servers fails, create a personal-fork automatically at each user grasp server and push there. fallback to prompting user for either grasp servers or git server with write permission. if user provides grasp servers, suggesting adding to user preference list.
Diffstat (limited to 'src/lib/login')
-rw-r--r--src/lib/login/user.rs77
1 files changed, 74 insertions, 3 deletions
diff --git a/src/lib/login/user.rs b/src/lib/login/user.rs
index 071cb25..0b702ef 100644
--- a/src/lib/login/user.rs
+++ b/src/lib/login/user.rs
@@ -1,7 +1,7 @@
1use std::{collections::HashSet, path::Path}; 1use std::{collections::HashSet, path::Path, sync::Arc};
2 2
3use anyhow::{Context, Result, bail}; 3use anyhow::{Context, Result, bail};
4use nostr::PublicKey; 4use nostr::{PublicKey, Url, event::Tag, signer::NostrSigner};
5use nostr_sdk::{Alphabet, JsonUtil, Kind, SingleLetterTag, Timestamp, ToBech32}; 5use nostr_sdk::{Alphabet, JsonUtil, Kind, SingleLetterTag, Timestamp, ToBech32};
6use serde::{self, Deserialize, Serialize}; 6use serde::{self, Deserialize, Serialize};
7 7
@@ -9,13 +9,17 @@ use serde::{self, Deserialize, Serialize};
9use crate::client::Client; 9use crate::client::Client;
10#[cfg(test)] 10#[cfg(test)]
11use crate::client::MockConnect; 11use crate::client::MockConnect;
12use crate::client::{Connect, get_event_from_global_cache}; 12use crate::{
13 client::{Connect, get_event_from_global_cache, sign_event},
14 git_events::KIND_USER_GRASP_LIST,
15};
13 16
14#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] 17#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
15pub struct UserRef { 18pub struct UserRef {
16 pub public_key: PublicKey, 19 pub public_key: PublicKey,
17 pub metadata: UserMetadata, 20 pub metadata: UserMetadata,
18 pub relays: UserRelays, 21 pub relays: UserRelays,
22 pub grasp_list: UserGraspList,
19} 23}
20 24
21#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] 25#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
@@ -49,6 +53,35 @@ impl UserRelays {
49} 53}
50 54
51#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] 55#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
56pub struct UserGraspList {
57 pub urls: Vec<Url>,
58 pub created_at: Timestamp,
59}
60
61impl UserGraspList {
62 pub async fn to_event(&mut self, signer: &Arc<dyn NostrSigner>) -> Result<nostr::Event> {
63 let event = sign_event(
64 nostr_sdk::EventBuilder::new(KIND_USER_GRASP_LIST, "").tags(
65 self.urls
66 .iter()
67 .map(|url| {
68 Tag::custom(
69 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("g")),
70 vec![url.to_string()],
71 )
72 })
73 .collect::<Vec<_>>(),
74 ),
75 signer,
76 "user grasp list".to_string(),
77 )
78 .await?;
79 self.created_at = event.created_at;
80 Ok(event)
81 }
82}
83
84#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
52pub struct UserRelayRef { 85pub struct UserRelayRef {
53 pub url: String, 86 pub url: String,
54 pub read: bool, 87 pub read: bool,
@@ -84,6 +117,7 @@ pub async fn get_user_details(
84 public_key: public_key.to_owned(), 117 public_key: public_key.to_owned(),
85 metadata: extract_user_metadata(public_key, &[])?, 118 metadata: extract_user_metadata(public_key, &[])?,
86 relays: extract_user_relays(public_key, &[]), 119 relays: extract_user_relays(public_key, &[]),
120 grasp_list: extract_user_grasp_list(public_key, &[]),
87 }; 121 };
88 if cache_only { 122 if cache_only {
89 Ok(empty) 123 Ok(empty)
@@ -117,6 +151,9 @@ pub async fn get_user_ref_from_cache(
117 nostr::Filter::default() 151 nostr::Filter::default()
118 .author(*public_key) 152 .author(*public_key)
119 .kind(Kind::RelayList), 153 .kind(Kind::RelayList),
154 nostr::Filter::default()
155 .author(*public_key)
156 .kind(KIND_USER_GRASP_LIST),
120 ]; 157 ];
121 158
122 let events = get_event_from_global_cache(git_repo_path, filters.clone()).await?; 159 let events = get_event_from_global_cache(git_repo_path, filters.clone()).await?;
@@ -128,6 +165,7 @@ pub async fn get_user_ref_from_cache(
128 public_key: public_key.to_owned(), 165 public_key: public_key.to_owned(),
129 metadata: extract_user_metadata(public_key, &events)?, 166 metadata: extract_user_metadata(public_key, &events)?,
130 relays: extract_user_relays(public_key, &events), 167 relays: extract_user_relays(public_key, &events),
168 grasp_list: extract_user_grasp_list(public_key, &events),
131 }) 169 })
132} 170}
133 171
@@ -215,3 +253,36 @@ pub fn extract_user_relays(public_key: &nostr::PublicKey, events: &[nostr::Event
215 }, 253 },
216 } 254 }
217} 255}
256
257pub fn extract_user_grasp_list(
258 public_key: &nostr::PublicKey,
259 events: &[nostr::Event],
260) -> UserGraspList {
261 let event = events
262 .iter()
263 .filter(|e| e.kind.eq(&KIND_USER_GRASP_LIST) && e.pubkey.eq(public_key))
264 .max_by_key(|e| e.created_at);
265
266 UserGraspList {
267 urls: if let Some(event) = event {
268 event
269 .tags
270 .iter()
271 .filter_map(|t| {
272 if t.as_slice().len() > 1 && t.as_slice()[0] == "g" {
273 Url::parse(&t.as_slice()[1]).ok()
274 } else {
275 None
276 }
277 })
278 .collect()
279 } else {
280 vec![]
281 },
282 created_at: if let Some(event) = event {
283 event.created_at
284 } else {
285 Timestamp::from(0)
286 },
287 }
288}