diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-08-01 14:18:00 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-08-01 14:18:00 +0100 |
| commit | fa16f46e341fca66c6c1b01cf28b2c5f2700b15b (patch) | |
| tree | c92036add46947fb23f0e45e7ac7659435aa2995 /src | |
| parent | 777a414d368b66363090a6d38beb95c821a4077b (diff) | |
feat(remote): add `nostr://npub/identifer`
support with optional relays as query parameter
Diffstat (limited to 'src')
| -rw-r--r-- | src/git_remote_helper.rs | 122 |
1 files changed, 113 insertions, 9 deletions
diff --git a/src/git_remote_helper.rs b/src/git_remote_helper.rs index ea220de..58c3bf8 100644 --- a/src/git_remote_helper.rs +++ b/src/git_remote_helper.rs | |||
| @@ -22,7 +22,7 @@ use client::{ | |||
| 22 | use git::RepoActions; | 22 | use git::RepoActions; |
| 23 | use git2::{Oid, Repository}; | 23 | use git2::{Oid, Repository}; |
| 24 | use nostr::nips::nip01::Coordinate; | 24 | use nostr::nips::nip01::Coordinate; |
| 25 | use nostr_sdk::{EventBuilder, Tag, Url}; | 25 | use nostr_sdk::{EventBuilder, PublicKey, Tag, Url}; |
| 26 | use nostr_signer::NostrSigner; | 26 | use nostr_signer::NostrSigner; |
| 27 | use repo_ref::RepoRef; | 27 | use repo_ref::RepoRef; |
| 28 | use repo_state::RepoState; | 28 | use repo_state::RepoState; |
| @@ -139,13 +139,53 @@ pub(crate) fn read_line<'a>(stdin: &io::Stdin, line: &'a mut String) -> io::Resu | |||
| 139 | 139 | ||
| 140 | fn nostr_git_url_to_repo_coordinates(url: &str) -> Result<HashSet<Coordinate>> { | 140 | fn nostr_git_url_to_repo_coordinates(url: &str) -> Result<HashSet<Coordinate>> { |
| 141 | let mut repo_coordinattes = HashSet::new(); | 141 | let mut repo_coordinattes = HashSet::new(); |
| 142 | let coordinate = Coordinate::parse(Url::parse(url)?.domain().context("no naddr")?)?; | 142 | let url = Url::parse(url)?; |
| 143 | if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { | 143 | |
| 144 | repo_coordinattes.insert(coordinate); | 144 | if url.scheme().ne("nostr") { |
| 145 | } else { | 145 | bail!("nostr git url must start with nostr://") |
| 146 | } | ||
| 147 | |||
| 148 | if let Ok(coordinate) = Coordinate::parse(url.domain().context("no naddr")?) { | ||
| 149 | if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { | ||
| 150 | repo_coordinattes.insert(coordinate); | ||
| 151 | return Ok(repo_coordinattes); | ||
| 152 | } | ||
| 146 | bail!("naddr doesnt point to a git repository announcement"); | 153 | bail!("naddr doesnt point to a git repository announcement"); |
| 147 | } | 154 | } |
| 148 | Ok(repo_coordinattes) | 155 | |
| 156 | if let Some(domain) = url.domain() { | ||
| 157 | if let Ok(public_key) = PublicKey::parse(domain) { | ||
| 158 | if url.path().is_empty() { | ||
| 159 | bail!( | ||
| 160 | "nostr git url should include the repo identifier eg nostr://npub123/the-repo-identifer" | ||
| 161 | ); | ||
| 162 | } | ||
| 163 | let mut relays = vec![]; | ||
| 164 | for (name, value) in url.query_pairs() { | ||
| 165 | if name.contains("relay") { | ||
| 166 | let mut decoded = urlencoding::decode(&value) | ||
| 167 | .context("could not parse relays in nostr git url")? | ||
| 168 | .to_string(); | ||
| 169 | if !decoded.starts_with("ws://") && !decoded.starts_with("wss://") { | ||
| 170 | decoded = format!("wss://{decoded}"); | ||
| 171 | } | ||
| 172 | let url = | ||
| 173 | Url::parse(&decoded).context("could not parse relays in nostr git url")?; | ||
| 174 | relays.push(url.to_string()); | ||
| 175 | } | ||
| 176 | } | ||
| 177 | repo_coordinattes.insert(Coordinate { | ||
| 178 | identifier: "ngit".to_string(), | ||
| 179 | public_key, | ||
| 180 | kind: nostr_sdk::Kind::GitRepoAnnouncement, | ||
| 181 | relays, | ||
| 182 | }); | ||
| 183 | return Ok(repo_coordinattes); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | bail!( | ||
| 187 | "nostr git url must be in format nostr://naddr123 or nostr://npub123/identifer?relay=wss://relay-example.com&relay1=wss://relay-example.org" | ||
| 188 | ); | ||
| 149 | } | 189 | } |
| 150 | 190 | ||
| 151 | async fn list(git_repo: &Repo, repo_ref: &RepoRef, for_push: bool) -> Result<()> { | 191 | async fn list(git_repo: &Repo, repo_ref: &RepoRef, for_push: bool) -> Result<()> { |
| @@ -528,7 +568,7 @@ mod tests { | |||
| 528 | 568 | ||
| 529 | use super::*; | 569 | use super::*; |
| 530 | 570 | ||
| 531 | fn get_model_coordinate() -> Coordinate { | 571 | fn get_model_coordinate(relays: bool) -> Coordinate { |
| 532 | Coordinate { | 572 | Coordinate { |
| 533 | identifier: "ngit".to_string(), | 573 | identifier: "ngit".to_string(), |
| 534 | public_key: PublicKey::parse( | 574 | public_key: PublicKey::parse( |
| @@ -536,7 +576,11 @@ mod tests { | |||
| 536 | ) | 576 | ) |
| 537 | .unwrap(), | 577 | .unwrap(), |
| 538 | kind: nostr_sdk::Kind::GitRepoAnnouncement, | 578 | kind: nostr_sdk::Kind::GitRepoAnnouncement, |
| 539 | relays: vec!["wss://nos.lol".to_string()], | 579 | relays: if relays { |
| 580 | vec!["wss://nos.lol/".to_string()] | ||
| 581 | } else { | ||
| 582 | vec![] | ||
| 583 | }, | ||
| 540 | } | 584 | } |
| 541 | } | 585 | } |
| 542 | 586 | ||
| @@ -546,9 +590,69 @@ mod tests { | |||
| 546 | nostr_git_url_to_repo_coordinates( | 590 | nostr_git_url_to_repo_coordinates( |
| 547 | "nostr://naddr1qqzxuemfwsqs6amnwvaz7tmwdaejumr0dspzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqvzqqqrhnym0k2qj" | 591 | "nostr://naddr1qqzxuemfwsqs6amnwvaz7tmwdaejumr0dspzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqvzqqqrhnym0k2qj" |
| 548 | )?, | 592 | )?, |
| 549 | HashSet::from([get_model_coordinate()]), | 593 | HashSet::from([get_model_coordinate(true)]), |
| 550 | ); | 594 | ); |
| 551 | Ok(()) | 595 | Ok(()) |
| 552 | } | 596 | } |
| 597 | mod from_npub_slah_identifier { | ||
| 598 | use super::*; | ||
| 599 | |||
| 600 | #[test] | ||
| 601 | fn without_relay() -> Result<()> { | ||
| 602 | assert_eq!( | ||
| 603 | nostr_git_url_to_repo_coordinates( | ||
| 604 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit" | ||
| 605 | )?, | ||
| 606 | HashSet::from([get_model_coordinate(false)]), | ||
| 607 | ); | ||
| 608 | Ok(()) | ||
| 609 | } | ||
| 610 | |||
| 611 | #[test] | ||
| 612 | fn with_relay_without_scheme_defaults_to_wss() -> Result<()> { | ||
| 613 | assert_eq!( | ||
| 614 | nostr_git_url_to_repo_coordinates( | ||
| 615 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay=nos.lol" | ||
| 616 | )?, | ||
| 617 | HashSet::from([get_model_coordinate(true)]), | ||
| 618 | ); | ||
| 619 | Ok(()) | ||
| 620 | } | ||
| 621 | |||
| 622 | #[test] | ||
| 623 | fn with_encoded_relay() -> Result<()> { | ||
| 624 | assert_eq!( | ||
| 625 | nostr_git_url_to_repo_coordinates(&format!( | ||
| 626 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}", | ||
| 627 | urlencoding::encode("wss://nos.lol") | ||
| 628 | ))?, | ||
| 629 | HashSet::from([get_model_coordinate(true)]), | ||
| 630 | ); | ||
| 631 | Ok(()) | ||
| 632 | } | ||
| 633 | #[test] | ||
| 634 | fn with_multiple_encoded_relays() -> Result<()> { | ||
| 635 | assert_eq!( | ||
| 636 | nostr_git_url_to_repo_coordinates(&format!( | ||
| 637 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}&relay1={}", | ||
| 638 | urlencoding::encode("wss://nos.lol"), | ||
| 639 | urlencoding::encode("wss://relay.damus.io"), | ||
| 640 | ))?, | ||
| 641 | HashSet::from([Coordinate { | ||
| 642 | identifier: "ngit".to_string(), | ||
| 643 | public_key: PublicKey::parse( | ||
| 644 | "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", | ||
| 645 | ) | ||
| 646 | .unwrap(), | ||
| 647 | kind: nostr_sdk::Kind::GitRepoAnnouncement, | ||
| 648 | relays: vec![ | ||
| 649 | "wss://nos.lol/".to_string(), | ||
| 650 | "wss://relay.damus.io/".to_string(), | ||
| 651 | ], | ||
| 652 | }]), | ||
| 653 | ); | ||
| 654 | Ok(()) | ||
| 655 | } | ||
| 656 | } | ||
| 553 | } | 657 | } |
| 554 | } | 658 | } |