diff options
| -rw-r--r-- | src/client.rs | 102 | ||||
| -rw-r--r-- | src/login.rs | 111 |
2 files changed, 123 insertions, 90 deletions
diff --git a/src/client.rs b/src/client.rs index b50ccf2..1fdc3d8 100644 --- a/src/client.rs +++ b/src/client.rs | |||
| @@ -35,7 +35,7 @@ use nostr_sqlite::SQLiteDatabase; | |||
| 35 | 35 | ||
| 36 | use crate::{ | 36 | use crate::{ |
| 37 | config::get_dirs, | 37 | config::get_dirs, |
| 38 | login::get_logged_in_user_and_relays_from_cache, | 38 | login::{get_logged_in_user, get_user_ref_from_cache}, |
| 39 | repo_ref::{RepoRef, REPO_REF_KIND}, | 39 | repo_ref::{RepoRef, REPO_REF_KIND}, |
| 40 | sub_commands::{ | 40 | sub_commands::{ |
| 41 | list::status_kinds, | 41 | list::status_kinds, |
| @@ -313,8 +313,8 @@ impl Connect for Client { | |||
| 313 | 313 | ||
| 314 | loop { | 314 | loop { |
| 315 | let relays = request | 315 | let relays = request |
| 316 | .relays | 316 | .repo_relays |
| 317 | .union(&request.current_user_write_relays) | 317 | .union(&request.user_relays_for_profiles) |
| 318 | // don't look for events on blaster | 318 | // don't look for events on blaster |
| 319 | .filter(|&r| !r.as_str().contains("nostr.mutinywallet.com")) | 319 | .filter(|&r| !r.as_str().contains("nostr.mutinywallet.com")) |
| 320 | .cloned() | 320 | .cloned() |
| @@ -325,11 +325,11 @@ impl Connect for Client { | |||
| 325 | if relays.is_empty() { | 325 | if relays.is_empty() { |
| 326 | break; | 326 | break; |
| 327 | } | 327 | } |
| 328 | let only_user_relays = request | 328 | let profile_relays_only = request |
| 329 | .current_user_write_relays | 329 | .user_relays_for_profiles |
| 330 | .difference(&request.relays) | 330 | .difference(&request.repo_relays) |
| 331 | .collect::<HashSet<&Url>>(); | 331 | .collect::<HashSet<&Url>>(); |
| 332 | for relay in &request.relays { | 332 | for relay in &request.repo_relays { |
| 333 | self.client | 333 | self.client |
| 334 | .add_relay(relay.as_str()) | 334 | .add_relay(relay.as_str()) |
| 335 | .await | 335 | .await |
| @@ -341,15 +341,17 @@ impl Connect for Client { | |||
| 341 | let futures: Vec<_> = relays | 341 | let futures: Vec<_> = relays |
| 342 | .iter() | 342 | .iter() |
| 343 | .map(|r| { | 343 | .map(|r| { |
| 344 | if only_user_relays.contains(r) { | 344 | if profile_relays_only.contains(r) { |
| 345 | // if user write relay isn't a repo relay, just filter for user profile | 345 | // if relay isn't a repo relay, just filter for user profile |
| 346 | FetchRequest { | 346 | FetchRequest { |
| 347 | selected_relay: Some(r.to_owned()), | 347 | selected_relay: Some(r.to_owned()), |
| 348 | repo_coordinates: vec![], | 348 | repo_coordinates: vec![], |
| 349 | proposals: HashSet::new(), | 349 | proposals: HashSet::new(), |
| 350 | missing_contributor_profiles: HashSet::from_iter(vec![ | 350 | missing_contributor_profiles: request |
| 351 | request.current_user.unwrap(), | 351 | .missing_contributor_profiles |
| 352 | ]), | 352 | .union(&request.profiles_to_fetch_from_user_relays) |
| 353 | .copied() | ||
| 354 | .collect(), | ||
| 353 | ..request.clone() | 355 | ..request.clone() |
| 354 | } | 356 | } |
| 355 | } else { | 357 | } else { |
| @@ -420,15 +422,26 @@ impl Connect for Client { | |||
| 420 | processed_relays.extend(relays.clone()); | 422 | processed_relays.extend(relays.clone()); |
| 421 | 423 | ||
| 422 | if let Ok(repo_ref) = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await { | 424 | if let Ok(repo_ref) = get_repo_ref_from_cache(git_repo_path, repo_coordinates).await { |
| 423 | request.relays = repo_ref | 425 | request.repo_relays = repo_ref |
| 424 | .relays | 426 | .relays |
| 425 | .iter() | 427 | .iter() |
| 426 | .filter_map(|r| Url::parse(r).ok()) | 428 | .filter_map(|r| Url::parse(r).ok()) |
| 427 | .collect(); | 429 | .collect(); |
| 428 | } | 430 | } |
| 429 | let (_, current_user_write_relays) = | 431 | |
| 430 | get_logged_in_user_and_relays_from_cache(git_repo_path).await?; | 432 | request.user_relays_for_profiles = { |
| 431 | request.current_user_write_relays = current_user_write_relays; | 433 | let mut set = HashSet::new(); |
| 434 | for user in &request.profiles_to_fetch_from_user_relays { | ||
| 435 | if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, user).await { | ||
| 436 | for r in user_ref.relays.write() { | ||
| 437 | if let Ok(url) = Url::parse(&r) { | ||
| 438 | set.insert(url); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | } | ||
| 442 | } | ||
| 443 | set | ||
| 444 | }; | ||
| 432 | } | 445 | } |
| 433 | Ok((relay_reports, progress_reporter)) | 446 | Ok((relay_reports, progress_reporter)) |
| 434 | } | 447 | } |
| @@ -444,10 +457,11 @@ impl Connect for Client { | |||
| 444 | fresh_coordinates.insert(c); | 457 | fresh_coordinates.insert(c); |
| 445 | } | 458 | } |
| 446 | let mut fresh_proposal_roots = request.proposals.clone(); | 459 | let mut fresh_proposal_roots = request.proposals.clone(); |
| 447 | let mut fresh_contributors = request.missing_contributor_profiles.clone(); | 460 | let mut fresh_contributors = request |
| 448 | if let Some(user) = request.current_user { | 461 | .missing_contributor_profiles |
| 449 | fresh_contributors.insert(user); | 462 | .union(&request.profiles_to_fetch_from_user_relays) |
| 450 | } | 463 | .copied() |
| 464 | .collect(); | ||
| 451 | 465 | ||
| 452 | let mut report = FetchReport::default(); | 466 | let mut report = FetchReport::default(); |
| 453 | 467 | ||
| @@ -870,19 +884,39 @@ async fn create_relays_request( | |||
| 870 | } | 884 | } |
| 871 | } | 885 | } |
| 872 | 886 | ||
| 873 | let (current_user, current_user_write_relays) = | 887 | let profiles_to_fetch_from_user_relays = { |
| 874 | get_logged_in_user_and_relays_from_cache(git_repo_path).await?; | 888 | let mut set = user_profiles.clone(); |
| 875 | if let Some(current_user) = current_user { | 889 | if let Ok(Some(current_user)) = get_logged_in_user(git_repo_path).await { |
| 876 | missing_contributor_profiles.insert(current_user); | 890 | set.insert(current_user); |
| 877 | } | 891 | } |
| 878 | missing_contributor_profiles.extend(user_profiles); | 892 | set |
| 893 | }; | ||
| 894 | |||
| 895 | let user_relays_for_profiles = { | ||
| 896 | let mut set = HashSet::new(); | ||
| 897 | for user in &profiles_to_fetch_from_user_relays { | ||
| 898 | if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, user).await { | ||
| 899 | for r in user_ref.relays.write() { | ||
| 900 | if let Ok(url) = Url::parse(&r) { | ||
| 901 | set.insert(url); | ||
| 902 | } | ||
| 903 | } | ||
| 904 | } else { | ||
| 905 | missing_contributor_profiles.insert(user.to_owned()); | ||
| 906 | } | ||
| 907 | } | ||
| 908 | set | ||
| 909 | }; | ||
| 879 | 910 | ||
| 880 | let existing_events: HashSet<EventId> = { | 911 | let existing_events: HashSet<EventId> = { |
| 881 | let mut existing_events: HashSet<EventId> = HashSet::new(); | 912 | let mut existing_events: HashSet<EventId> = HashSet::new(); |
| 882 | for filter in get_fetch_filters( | 913 | for filter in get_fetch_filters( |
| 883 | &repo_coordinates_without_relays, | 914 | &repo_coordinates_without_relays, |
| 884 | &proposals, | 915 | &proposals, |
| 885 | &missing_contributor_profiles, | 916 | &missing_contributor_profiles |
| 917 | .union(&profiles_to_fetch_from_user_relays) | ||
| 918 | .copied() | ||
| 919 | .collect(), | ||
| 886 | ) { | 920 | ) { |
| 887 | for (id, _) in get_local_cache_database(git_repo_path) | 921 | for (id, _) in get_local_cache_database(git_repo_path) |
| 888 | .await? | 922 | .await? |
| @@ -915,7 +949,7 @@ async fn create_relays_request( | |||
| 915 | }; | 949 | }; |
| 916 | 950 | ||
| 917 | let relay_column_width = relays | 951 | let relay_column_width = relays |
| 918 | .union(¤t_user_write_relays) | 952 | .union(&user_relays_for_profiles) |
| 919 | .reduce(|a, r| { | 953 | .reduce(|a, r| { |
| 920 | if r.to_string() | 954 | if r.to_string() |
| 921 | .chars() | 955 | .chars() |
| @@ -935,7 +969,7 @@ async fn create_relays_request( | |||
| 935 | 969 | ||
| 936 | Ok(FetchRequest { | 970 | Ok(FetchRequest { |
| 937 | selected_relay: None, | 971 | selected_relay: None, |
| 938 | relays, | 972 | repo_relays: relays, |
| 939 | relay_column_width, | 973 | relay_column_width, |
| 940 | repo_coordinates: if let Ok(repo_ref) = repo_ref { | 974 | repo_coordinates: if let Ok(repo_ref) = repo_ref { |
| 941 | repo_ref.coordinates_with_timestamps() | 975 | repo_ref.coordinates_with_timestamps() |
| @@ -949,8 +983,8 @@ async fn create_relays_request( | |||
| 949 | contributors, | 983 | contributors, |
| 950 | missing_contributor_profiles, | 984 | missing_contributor_profiles, |
| 951 | existing_events, | 985 | existing_events, |
| 952 | current_user, | 986 | profiles_to_fetch_from_user_relays, |
| 953 | current_user_write_relays, | 987 | user_relays_for_profiles, |
| 954 | }) | 988 | }) |
| 955 | } | 989 | } |
| 956 | 990 | ||
| @@ -1226,7 +1260,7 @@ impl Display for FetchReport { | |||
| 1226 | 1260 | ||
| 1227 | #[derive(Default, Clone)] | 1261 | #[derive(Default, Clone)] |
| 1228 | pub struct FetchRequest { | 1262 | pub struct FetchRequest { |
| 1229 | relays: HashSet<Url>, | 1263 | repo_relays: HashSet<Url>, |
| 1230 | selected_relay: Option<Url>, | 1264 | selected_relay: Option<Url>, |
| 1231 | relay_column_width: usize, | 1265 | relay_column_width: usize, |
| 1232 | repo_coordinates: Vec<(Coordinate, Option<Timestamp>)>, | 1266 | repo_coordinates: Vec<(Coordinate, Option<Timestamp>)>, |
| @@ -1234,6 +1268,6 @@ pub struct FetchRequest { | |||
| 1234 | contributors: HashSet<PublicKey>, | 1268 | contributors: HashSet<PublicKey>, |
| 1235 | missing_contributor_profiles: HashSet<PublicKey>, | 1269 | missing_contributor_profiles: HashSet<PublicKey>, |
| 1236 | existing_events: HashSet<EventId>, | 1270 | existing_events: HashSet<EventId>, |
| 1237 | current_user: Option<PublicKey>, | 1271 | profiles_to_fetch_from_user_relays: HashSet<PublicKey>, |
| 1238 | current_user_write_relays: HashSet<Url>, | 1272 | user_relays_for_profiles: HashSet<Url>, |
| 1239 | } | 1273 | } |
diff --git a/src/login.rs b/src/login.rs index 6c3acfa..aac0141 100644 --- a/src/login.rs +++ b/src/login.rs | |||
| @@ -6,7 +6,7 @@ use nostr::{ | |||
| 6 | PublicKey, | 6 | PublicKey, |
| 7 | }; | 7 | }; |
| 8 | use nostr_sdk::{ | 8 | use nostr_sdk::{ |
| 9 | Alphabet, FromBech32, JsonUtil, Keys, Kind, NostrSigner, SingleLetterTag, ToBech32, Url, | 9 | Alphabet, FromBech32, JsonUtil, Keys, Kind, NostrSigner, SingleLetterTag, ToBech32, |
| 10 | }; | 10 | }; |
| 11 | use nostr_signer::Nip46Signer; | 11 | use nostr_signer::Nip46Signer; |
| 12 | 12 | ||
| @@ -57,7 +57,8 @@ pub async fn launch( | |||
| 57 | get_config_item(git_repo, "nostr.npub") | 57 | get_config_item(git_repo, "nostr.npub") |
| 58 | .unwrap_or("unknown ncryptsec".to_string()), | 58 | .unwrap_or("unknown ncryptsec".to_string()), |
| 59 | ) { | 59 | ) { |
| 60 | if let Ok(user_ref) = get_user_details(&public_key, client, git_repo).await | 60 | if let Ok(user_ref) = |
| 61 | get_user_details(&public_key, client, git_repo.get_path()?).await | ||
| 61 | { | 62 | { |
| 62 | user_ref.metadata.name | 63 | user_ref.metadata.name |
| 63 | } else { | 64 | } else { |
| @@ -92,7 +93,7 @@ pub async fn launch( | |||
| 92 | .await | 93 | .await |
| 93 | .context("cannot get public key from signer")?, | 94 | .context("cannot get public key from signer")?, |
| 94 | client, | 95 | client, |
| 95 | git_repo, | 96 | git_repo.get_path()?, |
| 96 | ) | 97 | ) |
| 97 | .await?; | 98 | .await?; |
| 98 | print_logged_in_as(&user_ref, client.is_none())?; | 99 | print_logged_in_as(&user_ref, client.is_none())?; |
| @@ -395,7 +396,7 @@ async fn fresh_login( | |||
| 395 | signer.public_key().await? | 396 | signer.public_key().await? |
| 396 | }; | 397 | }; |
| 397 | // lookup profile | 398 | // lookup profile |
| 398 | let user_ref = get_user_details(&public_key, client, git_repo).await?; | 399 | let user_ref = get_user_details(&public_key, client, git_repo.get_path()?).await?; |
| 399 | print_logged_in_as(&user_ref, client.is_none())?; | 400 | print_logged_in_as(&user_ref, client.is_none())?; |
| 400 | Ok((signer, user_ref)) | 401 | Ok((signer, user_ref)) |
| 401 | } | 402 | } |
| @@ -610,77 +611,75 @@ async fn get_user_details( | |||
| 610 | public_key: &PublicKey, | 611 | public_key: &PublicKey, |
| 611 | #[cfg(test)] client: Option<&crate::client::MockConnect>, | 612 | #[cfg(test)] client: Option<&crate::client::MockConnect>, |
| 612 | #[cfg(not(test))] client: Option<&Client>, | 613 | #[cfg(not(test))] client: Option<&Client>, |
| 613 | git_repo: &Repo, | 614 | git_repo_path: &Path, |
| 614 | ) -> Result<UserRef> { | 615 | ) -> Result<UserRef> { |
| 615 | let filters = vec![ | 616 | if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, public_key).await { |
| 616 | nostr::Filter::default() | 617 | Ok(user_ref) |
| 617 | .author(*public_key) | 618 | } else { |
| 618 | .kind(Kind::Metadata), | 619 | let empty = UserRef { |
| 619 | nostr::Filter::default() | 620 | public_key: public_key.to_owned(), |
| 620 | .author(*public_key) | 621 | metadata: extract_user_metadata(public_key, &[])?, |
| 621 | .kind(Kind::RelayList), | 622 | relays: extract_user_relays(public_key, &[]), |
| 622 | ]; | 623 | }; |
| 623 | |||
| 624 | let mut events = get_event_from_global_cache(git_repo.get_path()?, filters.clone()).await?; | ||
| 625 | 624 | ||
| 626 | if let Some(client) = client { | 625 | if let Some(client) = client { |
| 627 | if events.is_empty() { | ||
| 628 | let term = console::Term::stderr(); | 626 | let term = console::Term::stderr(); |
| 629 | term.write_line("searching for profile...")?; | 627 | term.write_line("searching for profile...")?; |
| 630 | let (_, progress_reporter) = client | 628 | let (_, progress_reporter) = client |
| 631 | .fetch_all( | 629 | .fetch_all( |
| 632 | git_repo.get_path()?, | 630 | git_repo_path, |
| 633 | &HashSet::new(), | 631 | &HashSet::new(), |
| 634 | &HashSet::from_iter(vec![*public_key]), | 632 | &HashSet::from_iter(vec![*public_key]), |
| 635 | ) | 633 | ) |
| 636 | .await?; | 634 | .await?; |
| 637 | events = get_event_from_global_cache(git_repo.get_path()?, filters).await?; | 635 | if let Ok(user_ref) = get_user_ref_from_cache(git_repo_path, public_key).await { |
| 638 | if !events.is_empty() { | ||
| 639 | progress_reporter.clear()?; | 636 | progress_reporter.clear()?; |
| 640 | // term.clear_last_lines(1)?; | 637 | // term.clear_last_lines(1)?; |
| 638 | Ok(user_ref) | ||
| 639 | } else { | ||
| 640 | Ok(empty) | ||
| 641 | } | 641 | } |
| 642 | } else { | ||
| 643 | Ok(empty) | ||
| 642 | } | 644 | } |
| 643 | } | 645 | } |
| 646 | } | ||
| 647 | pub async fn get_logged_in_user(git_repo_path: &Path) -> Result<Option<PublicKey>> { | ||
| 648 | let git_repo = Repo::from_path(&git_repo_path.to_path_buf())?; | ||
| 649 | Ok( | ||
| 650 | if let Some(npub) = git_repo.get_git_config_item("nostr.npub", None)? { | ||
| 651 | if let Ok(pubic_key) = PublicKey::parse(npub) { | ||
| 652 | Some(pubic_key) | ||
| 653 | } else { | ||
| 654 | None | ||
| 655 | } | ||
| 656 | } else { | ||
| 657 | None | ||
| 658 | }, | ||
| 659 | ) | ||
| 660 | } | ||
| 661 | |||
| 662 | pub async fn get_user_ref_from_cache( | ||
| 663 | git_repo_path: &Path, | ||
| 664 | public_key: &PublicKey, | ||
| 665 | ) -> Result<UserRef> { | ||
| 666 | let filters = vec![ | ||
| 667 | nostr::Filter::default() | ||
| 668 | .author(*public_key) | ||
| 669 | .kind(Kind::Metadata), | ||
| 670 | nostr::Filter::default() | ||
| 671 | .author(*public_key) | ||
| 672 | .kind(Kind::RelayList), | ||
| 673 | ]; | ||
| 674 | |||
| 675 | let events = get_event_from_global_cache(git_repo_path, filters.clone()).await?; | ||
| 644 | 676 | ||
| 677 | if events.is_empty() { | ||
| 678 | bail!("no metadata and profile list in cache for selected public key"); | ||
| 679 | } | ||
| 645 | Ok(UserRef { | 680 | Ok(UserRef { |
| 646 | public_key: public_key.to_owned(), | 681 | public_key: public_key.to_owned(), |
| 647 | metadata: extract_user_metadata(public_key, &events)?, | 682 | metadata: extract_user_metadata(public_key, &events)?, |
| 648 | relays: extract_user_relays(public_key, &events), | 683 | relays: extract_user_relays(public_key, &events), |
| 649 | }) | 684 | }) |
| 650 | } | 685 | } |
| 651 | |||
| 652 | pub async fn get_logged_in_user_and_relays_from_cache( | ||
| 653 | git_repo_path: &Path, | ||
| 654 | ) -> Result<(Option<PublicKey>, HashSet<Url>)> { | ||
| 655 | let git_repo = Repo::from_path(&git_repo_path.to_path_buf())?; | ||
| 656 | let current_user = if let Some(npub) = git_repo.get_git_config_item("nostr.npub", None)? { | ||
| 657 | if let Ok(pubic_key) = PublicKey::parse(npub) { | ||
| 658 | Some(pubic_key) | ||
| 659 | } else { | ||
| 660 | None | ||
| 661 | } | ||
| 662 | } else { | ||
| 663 | None | ||
| 664 | }; | ||
| 665 | let relays = if let Some(current_user) = current_user { | ||
| 666 | extract_user_relays( | ||
| 667 | ¤t_user, | ||
| 668 | &get_event_from_global_cache( | ||
| 669 | git_repo.get_path()?, | ||
| 670 | vec![ | ||
| 671 | nostr::Filter::default() | ||
| 672 | .author((*current_user).into()) | ||
| 673 | .kind(Kind::RelayList), | ||
| 674 | ], | ||
| 675 | ) | ||
| 676 | .await?, | ||
| 677 | ) | ||
| 678 | .write() | ||
| 679 | .iter() | ||
| 680 | .filter_map(|r| Url::parse(r).ok()) | ||
| 681 | .collect::<HashSet<Url>>() | ||
| 682 | } else { | ||
| 683 | HashSet::new() | ||
| 684 | }; | ||
| 685 | Ok((current_user, relays)) | ||
| 686 | } | ||