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>2025-05-23 10:01:29 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2025-05-23 10:01:29 +0100
commit686604665395385600ef8f1b5238a775249552a1 (patch)
tree56a4c7a956e14dfbcdd4a518096968abc69583a6 /src/lib
parentcc9a3a1d8526373625246504f72f338fd89c8d8b (diff)
feat: only try http(s) for ngit-relays
otherwise it tries all the protocols and reprots on each
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/repo_ref.rs180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/lib/repo_ref.rs b/src/lib/repo_ref.rs
index df1427a..aa36bbb 100644
--- a/src/lib/repo_ref.rs
+++ b/src/lib/repo_ref.rs
@@ -290,6 +290,10 @@ impl RepoRef {
290 user: None, 290 user: None,
291 } 291 }
292 } 292 }
293
294 pub fn ngit_relays(&self) -> Vec<String> {
295 detect_existing_ngit_relays(Some(self), &[], &[], &[], &self.identifier)
296 }
293} 297}
294 298
295pub async fn get_repo_coordinates_when_remote_unknown( 299pub async fn get_repo_coordinates_when_remote_unknown(
@@ -565,6 +569,145 @@ pub fn save_repo_config_to_yaml(
565 .context("failed to write maintainers to maintainers.yaml file serde_yaml") 569 .context("failed to write maintainers to maintainers.yaml file serde_yaml")
566} 570}
567 571
572pub fn detect_existing_ngit_relays(
573 repo_ref: Option<&RepoRef>,
574 args_relays: &[String],
575 args_clone_url: &[String],
576 args_blossoms: &[String],
577 identifier: &str,
578) -> Vec<String> {
579 // Collect clone URLs from arguments or repo_ref
580 let clone_urls: Vec<String> = if !args_clone_url.is_empty() {
581 args_clone_url.to_vec()
582 } else if let Some(repo) = repo_ref {
583 repo.git_server.clone()
584 } else {
585 Vec::new()
586 };
587
588 // Collect relays from arguments or repo_ref
589 let relays: Vec<RelayUrl> = if !args_relays.is_empty() {
590 args_relays
591 .iter()
592 .filter_map(|r| RelayUrl::parse(r).ok())
593 .collect()
594 } else if let Some(repo) = repo_ref {
595 repo.relays.clone()
596 } else {
597 Vec::new()
598 };
599
600 // Collect blossom server URLs from arguments or repo_ref
601 let blossoms: Vec<Url> = if !args_blossoms.is_empty() {
602 args_blossoms
603 .iter()
604 .filter_map(|r| Url::parse(r).ok())
605 .collect()
606 } else if let Some(repo) = repo_ref {
607 repo.blossoms.clone()
608 } else {
609 Vec::new()
610 };
611
612 let mut existing_ngit_relays = Vec::new();
613 for url in &clone_urls {
614 let Ok(formatted_as_ngit_relay_url) = normalize_ngit_relay_url(url) else {
615 continue;
616 };
617 if existing_ngit_relays.contains(&formatted_as_ngit_relay_url) {
618 continue;
619 }
620
621 let clone_url_is_ngit_relay_format = if let Ok(npub) = extract_npub(url) {
622 url.contains(&format!("/{npub}/{identifier}.git"))
623 } else {
624 false
625 };
626 if !clone_url_is_ngit_relay_format {
627 continue;
628 }
629
630 let matches_relay = relays.iter().any(|r| {
631 normalize_ngit_relay_url(&r.to_string())
632 .is_ok_and(|r| r.eq(&formatted_as_ngit_relay_url))
633 });
634 if !matches_relay {
635 continue;
636 }
637
638 let matches_blossoms = blossoms.iter().any(|r| {
639 normalize_ngit_relay_url(r.as_str()).is_ok_and(|r| r.eq(&formatted_as_ngit_relay_url))
640 });
641 if !matches_blossoms {
642 continue;
643 }
644
645 existing_ngit_relays.push(formatted_as_ngit_relay_url);
646 }
647 existing_ngit_relays
648}
649
650pub fn normalize_ngit_relay_url(url: &str) -> Result<String> {
651 // Parse the URL and handle errors
652 let mut parsed = Url::parse(url)
653 .or_else(|_| Url::parse(&format!("https://{url}")))
654 .context(format!("{url} not a valid ngit relay URL"))?;
655 if parsed.host_str().is_none() {
656 // so sub.domain.org gets identifier as host in "sub.domain.org"
657 parsed = Url::parse(&format!("https://{url}"))?;
658 }
659
660 // Extract the scheme, host, port, and path
661 let scheme = parsed.scheme();
662 let host = parsed.host_str().context(format!(
663 "{url} not a ngit relay url reference: missing host in URL {parsed}"
664 ))?;
665 let port = parsed.port().map(|p| format!(":{p}")).unwrap_or_default();
666 let path = parsed.path();
667
668 // Normalize the URL based on the scheme and path
669 let mut normalized_url = match scheme {
670 "ws" | "http" => format!("http://{host}{port}{path}"),
671 _ => format!("{host}{port}{path}"),
672 };
673
674 // If the normalized URL contains "npub1", remove "npub1" and everything after
675 // it
676 if let Some(pos) = normalized_url.find("npub1") {
677 normalized_url.truncate(pos); // Keep everything before "npub1"
678 }
679 // Return the normalized URL
680 Ok(normalized_url.trim_end_matches('/').to_string())
681}
682
683pub fn extract_npub(s: &str) -> Result<&str> {
684 // Find the starting index of "npub1"
685 if let Some(start) = s.find("npub1") {
686 let mut end = start + 5; // Start after "npub1"
687
688 // Move the end index to include valid characters (0-9, a-z)
689 while end < s.len() && s[end..=end].chars().all(|c| c.is_ascii_alphanumeric()) {
690 end += 1;
691 }
692 // Extract the npub substring
693 let npub = &s[start..end];
694 // Attempt to create a PublicKey from the extracted npub
695 PublicKey::from_bech32(npub).context("invalid npub")?;
696 Ok(npub)
697 } else {
698 bail!("No npub found")
699 }
700}
701
702pub fn is_ngit_relay(url: &str, ngit_relays: &[String]) -> bool {
703 if !ngit_relays.is_empty() {
704 if let Ok(n) = normalize_ngit_relay_url(url) {
705 return ngit_relays.contains(&n);
706 }
707 }
708 false
709}
710
568#[cfg(test)] 711#[cfg(test)]
569mod tests { 712mod tests {
570 use test_utils::*; 713 use test_utils::*;
@@ -842,4 +985,41 @@ mod tests {
842 } 985 }
843 } 986 }
844 } 987 }
988
989 #[test]
990 fn normalize_ngit_relay_url_all_checks() -> Result<()> {
991 let test_cases = vec![
992 ("https://sub.domain.org", "sub.domain.org"),
993 ("wss://sub.domain.org", "sub.domain.org"),
994 ("sub.domain.org", "sub.domain.org"),
995 ("http://sub.domain.org", "http://sub.domain.org"),
996 ("ws://sub.domain.org", "http://sub.domain.org"),
997 ("http://localhost", "http://localhost"),
998 ("localhost", "localhost"),
999 ("https://sub.domain.org:8080", "sub.domain.org:8080"),
1000 ("http://sub.domain.org:8080", "http://sub.domain.org:8080"),
1001 ("sub.domain.org:8080", "sub.domain.org:8080"),
1002 ("https://sub.domain.org/path/to", "sub.domain.org/path/to"),
1003 (
1004 "https://sub.domain.org:8080/path/to",
1005 "sub.domain.org:8080/path/to",
1006 ),
1007 (
1008 "https://sub.domain.org/npub143675782648/to.git",
1009 "sub.domain.org",
1010 ),
1011 (
1012 "https://sub.domain.org/path/npub143675782648/to.git",
1013 "sub.domain.org/path",
1014 ),
1015 ("https://sub.domain.org/", "sub.domain.org"),
1016 ("http://sub.domain.org/", "http://sub.domain.org"),
1017 ];
1018
1019 for (input, expected) in test_cases {
1020 let normalized = normalize_ngit_relay_url(input)?;
1021 assert_eq!(normalized, expected);
1022 }
1023 Ok(())
1024 }
845} 1025}