upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-19 17:01:36 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-19 17:01:36 +0000
commitbf7f4d5381203d5c27b2811d62c5b1781533aa2b (patch)
tree26903bbf535d83abd7242370d8b6932eb80e3389 /src
parentfa065ad128882755f2a988d6203b59a2ab5e38ff (diff)
fix some clippy fmt warnings
Diffstat (limited to 'src')
-rw-r--r--src/config.rs6
-rw-r--r--src/http/landing.rs15
-rw-r--r--src/http/mod.rs11
-rw-r--r--src/http/websocket.rs24
-rw-r--r--src/nostr/events.rs74
5 files changed, 57 insertions, 73 deletions
diff --git a/src/config.rs b/src/config.rs
index 252873d..b4dd853 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -19,10 +19,8 @@ impl Config {
19 dotenvy::dotenv().ok(); 19 dotenvy::dotenv().ok();
20 20
21 Ok(Config { 21 Ok(Config {
22 domain: env::var("NGIT_DOMAIN") 22 domain: env::var("NGIT_DOMAIN").unwrap_or_else(|_| "localhost:8080".to_string()),
23 .unwrap_or_else(|_| "localhost:8080".to_string()), 23 owner_npub: env::var("NGIT_OWNER_NPUB").context("NGIT_OWNER_NPUB must be set")?,
24 owner_npub: env::var("NGIT_OWNER_NPUB")
25 .context("NGIT_OWNER_NPUB must be set")?,
26 relay_name: env::var("NGIT_RELAY_NAME") 24 relay_name: env::var("NGIT_RELAY_NAME")
27 .unwrap_or_else(|_| "ngit-grasp relay".to_string()), 25 .unwrap_or_else(|_| "ngit-grasp relay".to_string()),
28 relay_description: env::var("NGIT_RELAY_DESCRIPTION") 26 relay_description: env::var("NGIT_RELAY_DESCRIPTION")
diff --git a/src/http/landing.rs b/src/http/landing.rs
index 35e49e5..976ec50 100644
--- a/src/http/landing.rs
+++ b/src/http/landing.rs
@@ -1,7 +1,6 @@
1/// Landing Page Handler 1/// Landing Page Handler
2/// 2///
3/// Serves the HTML landing page or upgrades to WebSocket for Nostr relay connections. 3/// Serves the HTML landing page or upgrades to WebSocket for Nostr relay connections.
4
5use actix_web::{web, HttpRequest, HttpResponse, Result}; 4use actix_web::{web, HttpRequest, HttpResponse, Result};
6use nostr_relay_builder::LocalRelay; 5use nostr_relay_builder::LocalRelay;
7 6
@@ -16,12 +15,16 @@ pub async fn handle(
16) -> Result<HttpResponse> { 15) -> Result<HttpResponse> {
17 // Check if this is a WebSocket upgrade request 16 // Check if this is a WebSocket upgrade request
18 if let Some(upgrade) = req.headers().get("upgrade") { 17 if let Some(upgrade) = req.headers().get("upgrade") {
19 if upgrade.to_str().unwrap_or("").eq_ignore_ascii_case("websocket") { 18 if upgrade
19 .to_str()
20 .unwrap_or("")
21 .eq_ignore_ascii_case("websocket")
22 {
20 // Delegate to WebSocket handler 23 // Delegate to WebSocket handler
21 return crate::http::websocket::handle(req, stream, relay).await; 24 return crate::http::websocket::handle(req, stream, relay).await;
22 } 25 }
23 } 26 }
24 27
25 // Otherwise, serve the landing page 28 // Otherwise, serve the landing page
26 let html = format!( 29 let html = format!(
27 include_str!("../../templates/landing.html"), 30 include_str!("../../templates/landing.html"),
@@ -30,8 +33,8 @@ pub async fn handle(
30 domain = config.domain, 33 domain = config.domain,
31 bind_address = config.bind_address, 34 bind_address = config.bind_address,
32 ); 35 );
33 36
34 Ok(HttpResponse::Ok() 37 Ok(HttpResponse::Ok()
35 .content_type("text/html; charset=utf-8") 38 .content_type("text/html; charset=utf-8")
36 .body(html)) 39 .body(html))
37} \ No newline at end of file 40}
diff --git a/src/http/mod.rs b/src/http/mod.rs
index 286e8ff..b434c69 100644
--- a/src/http/mod.rs
+++ b/src/http/mod.rs
@@ -1,7 +1,6 @@
1/// HTTP Server Module 1/// HTTP Server Module
2/// 2///
3/// Provides actix-web HTTP server with WebSocket upgrade support for the Nostr relay. 3/// Provides actix-web HTTP server with WebSocket upgrade support for the Nostr relay.
4
5pub mod landing; 4pub mod landing;
6pub mod websocket; 5pub mod websocket;
7 6
@@ -13,9 +12,9 @@ use crate::config::Config;
13/// Start the HTTP server with integrated Nostr relay 12/// Start the HTTP server with integrated Nostr relay
14pub async fn run_server(config: Config, relay: LocalRelay) -> anyhow::Result<()> { 13pub async fn run_server(config: Config, relay: LocalRelay) -> anyhow::Result<()> {
15 let bind_addr = config.bind_address.clone(); 14 let bind_addr = config.bind_address.clone();
16 15
17 tracing::info!("Starting HTTP server on {}", bind_addr); 16 tracing::info!("Starting HTTP server on {}", bind_addr);
18 17
19 HttpServer::new(move || { 18 HttpServer::new(move || {
20 App::new() 19 App::new()
21 .app_data(web::Data::new(config.clone())) 20 .app_data(web::Data::new(config.clone()))
@@ -26,6 +25,6 @@ pub async fn run_server(config: Config, relay: LocalRelay) -> anyhow::Result<()>
26 .bind(&bind_addr)? 25 .bind(&bind_addr)?
27 .run() 26 .run()
28 .await?; 27 .await?;
29 28
30 Ok(()) 29 Ok(())
31} \ No newline at end of file 30}
diff --git a/src/http/websocket.rs b/src/http/websocket.rs
index 7af847a..0171013 100644
--- a/src/http/websocket.rs
+++ b/src/http/websocket.rs
@@ -1,8 +1,7 @@
1/// WebSocket Handler 1/// WebSocket Handler
2/// 2///
3/// Handles WebSocket upgrade requests and passes connections to the Nostr relay. 3/// Handles WebSocket upgrade requests and passes connections to the Nostr relay.
4 4use actix_web::{web, Error, HttpRequest, HttpResponse, Result};
5use actix_web::{web, HttpRequest, HttpResponse, Result, Error};
6use actix_ws::Message; 5use actix_ws::Message;
7use futures_util::StreamExt; 6use futures_util::StreamExt;
8use nostr_relay_builder::LocalRelay; 7use nostr_relay_builder::LocalRelay;
@@ -14,19 +13,20 @@ pub async fn handle(
14 relay: web::Data<LocalRelay>, 13 relay: web::Data<LocalRelay>,
15) -> Result<HttpResponse, Error> { 14) -> Result<HttpResponse, Error> {
16 let (response, mut session, mut msg_stream) = actix_ws::handle(&req, stream)?; 15 let (response, mut session, mut msg_stream) = actix_ws::handle(&req, stream)?;
17 16
18 let peer_addr = req.peer_addr() 17 let peer_addr = req
18 .peer_addr()
19 .unwrap_or_else(|| "0.0.0.0:0".parse().unwrap()); 19 .unwrap_or_else(|| "0.0.0.0:0".parse().unwrap());
20 20
21 tracing::debug!("WebSocket connection from {}", peer_addr); 21 tracing::debug!("WebSocket connection from {}", peer_addr);
22 22
23 // Spawn task to handle the WebSocket connection 23 // Spawn task to handle the WebSocket connection
24 // TODO: Will use relay.take_connection() for full Nostr relay integration 24 // TODO: Will use relay.take_connection() for full Nostr relay integration
25 let _relay = relay.get_ref().clone(); 25 let _relay = relay.get_ref().clone();
26 actix_web::rt::spawn(async move { 26 actix_web::rt::spawn(async move {
27 // Create a channel to communicate between actix-ws and relay 27 // Create a channel to communicate between actix-ws and relay
28 let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); 28 let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
29 29
30 // Spawn task to send messages from relay to client 30 // Spawn task to send messages from relay to client
31 let mut session_clone = session.clone(); 31 let mut session_clone = session.clone();
32 actix_web::rt::spawn(async move { 32 actix_web::rt::spawn(async move {
@@ -36,7 +36,7 @@ pub async fn handle(
36 } 36 }
37 } 37 }
38 }); 38 });
39 39
40 // Handle incoming messages from client 40 // Handle incoming messages from client
41 while let Some(Ok(msg)) = msg_stream.next().await { 41 while let Some(Ok(msg)) = msg_stream.next().await {
42 match msg { 42 match msg {
@@ -65,9 +65,9 @@ pub async fn handle(
65 Message::Nop => {} 65 Message::Nop => {}
66 } 66 }
67 } 67 }
68 68
69 tracing::debug!("WebSocket connection closed for {}", peer_addr); 69 tracing::debug!("WebSocket connection closed for {}", peer_addr);
70 }); 70 });
71 71
72 Ok(response) 72 Ok(response)
73} \ No newline at end of file 73}
diff --git a/src/nostr/events.rs b/src/nostr/events.rs
index 88aefed..21dd2dd 100644
--- a/src/nostr/events.rs
+++ b/src/nostr/events.rs
@@ -1,12 +1,11 @@
1/// NIP-34 Git Repository Event Handling 1/// NIP-34 Git Repository Event Handling
2/// 2///
3/// This module handles Git repository announcements (kind 30617) and 3/// This module handles Git repository announcements (kind 30617) and
4/// repository state announcements (kind 30618) according to NIP-34 and GRASP-01. 4/// repository state announcements (kind 30618) according to NIP-34 and GRASP-01.
5/// 5///
6/// Reference: 6/// Reference:
7/// - NIP-34: https://nips.nostr.com/34 7/// - NIP-34: https://nips.nostr.com/34
8/// - GRASP-01: https://gitworkshop.dev/danconwaydev.com/grasp/01.md 8/// - GRASP-01: https://gitworkshop.dev/danconwaydev.com/grasp/01.md
9
10use anyhow::{anyhow, Result}; 9use anyhow::{anyhow, Result};
11use nostr_sdk::{Event, Kind, TagKind, ToBech32}; 10use nostr_sdk::{Event, Kind, TagKind, ToBech32};
12 11
@@ -138,8 +137,8 @@ impl RepositoryAnnouncement {
138 } 137 }
139 138
140 /// Check if this announcement lists the service (both clone and relay) 139 /// Check if this announcement lists the service (both clone and relay)
141 /// 140 ///
142 /// GRASP-01 requirement: MUST reject announcements that do not list 141 /// GRASP-01 requirement: MUST reject announcements that do not list
143 /// the service in both `clone` and `relays` tags unless implementing GRASP-05. 142 /// the service in both `clone` and `relays` tags unless implementing GRASP-05.
144 pub fn lists_service(&self, domain: &str) -> bool { 143 pub fn lists_service(&self, domain: &str) -> bool {
145 self.has_clone_url(domain) && self.has_relay(domain) 144 self.has_clone_url(domain) && self.has_relay(domain)
@@ -278,19 +277,19 @@ impl RepositoryState {
278} 277}
279 278
280/// Validate a repository announcement according to GRASP-01 279/// Validate a repository announcement according to GRASP-01
281/// 280///
282/// Returns Ok(()) if valid, Err with reason if invalid. 281/// Returns Ok(()) if valid, Err with reason if invalid.
283pub fn validate_announcement(event: &Event, domain: &str) -> Result<()> { 282pub fn validate_announcement(event: &Event, domain: &str) -> Result<()> {
284 // Must be kind 30617 283 // Must be kind 30617
285 if event.kind != Kind::from(KIND_REPOSITORY_ANNOUNCEMENT) { 284 if event.kind != Kind::from(KIND_REPOSITORY_ANNOUNCEMENT) {
286 return Err(anyhow!("Invalid kind: expected {}", KIND_REPOSITORY_ANNOUNCEMENT)); 285 return Err(anyhow!(
286 "Invalid kind: expected {}",
287 KIND_REPOSITORY_ANNOUNCEMENT
288 ));
287 } 289 }
288 290
289 // Must have identifier 291 // Must have identifier
290 let has_identifier = event 292 let has_identifier = event.tags.iter().any(|t| t.kind() == TagKind::d());
291 .tags
292 .iter()
293 .any(|t| t.kind() == TagKind::d());
294 if !has_identifier { 293 if !has_identifier {
295 return Err(anyhow!("Missing required 'd' tag (identifier)")); 294 return Err(anyhow!("Missing required 'd' tag (identifier)"));
296 } 295 }
@@ -298,7 +297,7 @@ pub fn validate_announcement(event: &Event, domain: &str) -> Result<()> {
298 // Parse full announcement to validate structure 297 // Parse full announcement to validate structure
299 let announcement = RepositoryAnnouncement::from_event(event.clone())?; 298 let announcement = RepositoryAnnouncement::from_event(event.clone())?;
300 299
301 // GRASP-01: MUST reject announcements that do not list the service 300 // GRASP-01: MUST reject announcements that do not list the service
302 // in both `clone` and `relays` tags unless implementing GRASP-05 301 // in both `clone` and `relays` tags unless implementing GRASP-05
303 if !announcement.lists_service(domain) { 302 if !announcement.lists_service(domain) {
304 return Err(anyhow!( 303 return Err(anyhow!(
@@ -313,7 +312,7 @@ pub fn validate_announcement(event: &Event, domain: &str) -> Result<()> {
313} 312}
314 313
315/// Validate a repository state announcement according to GRASP-01 314/// Validate a repository state announcement according to GRASP-01
316/// 315///
317/// Returns Ok(()) if valid, Err with reason if invalid. 316/// Returns Ok(()) if valid, Err with reason if invalid.
318pub fn validate_state(event: &Event) -> Result<()> { 317pub fn validate_state(event: &Event) -> Result<()> {
319 // Must be kind 30618 318 // Must be kind 30618
@@ -322,10 +321,7 @@ pub fn validate_state(event: &Event) -> Result<()> {
322 } 321 }
323 322
324 // Must have identifier 323 // Must have identifier
325 let has_identifier = event 324 let has_identifier = event.tags.iter().any(|t| t.kind() == TagKind::d());
326 .tags
327 .iter()
328 .any(|t| t.kind() == TagKind::d());
329 if !has_identifier { 325 if !has_identifier {
330 return Err(anyhow!("Missing required 'd' tag (identifier)")); 326 return Err(anyhow!("Missing required 'd' tag (identifier)"));
331 } 327 }
@@ -352,7 +348,7 @@ mod tests {
352 relays: Vec<&str>, 348 relays: Vec<&str>,
353 ) -> Event { 349 ) -> Event {
354 use nostr_sdk::Tag; 350 use nostr_sdk::Tag;
355 351
356 let mut tags = vec![Tag::custom( 352 let mut tags = vec![Tag::custom(
357 nostr_sdk::TagKind::d(), 353 nostr_sdk::TagKind::d(),
358 vec![identifier.to_string()], 354 vec![identifier.to_string()],
@@ -372,18 +368,15 @@ mod tests {
372 )); 368 ));
373 } 369 }
374 370
375 EventBuilder::new( 371 EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repository")
376 Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), 372 .tags(tags)
377 "Test repository", 373 .sign_with_keys(keys)
378 ) 374 .unwrap()
379 .tags(tags)
380 .sign_with_keys(keys)
381 .unwrap()
382 } 375 }
383 376
384 fn create_state_event(keys: &Keys, identifier: &str, branches: Vec<(&str, &str)>) -> Event { 377 fn create_state_event(keys: &Keys, identifier: &str, branches: Vec<(&str, &str)>) -> Event {
385 use nostr_sdk::Tag; 378 use nostr_sdk::Tag;
386 379
387 let mut tags = vec![Tag::custom( 380 let mut tags = vec![Tag::custom(
388 nostr_sdk::TagKind::d(), 381 nostr_sdk::TagKind::d(),
389 vec![identifier.to_string()], 382 vec![identifier.to_string()],
@@ -392,10 +385,7 @@ mod tests {
392 for (branch, commit) in branches { 385 for (branch, commit) in branches {
393 tags.push(Tag::custom( 386 tags.push(Tag::custom(
394 nostr_sdk::TagKind::Custom("ref".into()), 387 nostr_sdk::TagKind::Custom("ref".into()),
395 vec![ 388 vec![format!("refs/heads/{}", branch), commit.to_string()],
396 format!("refs/heads/{}", branch),
397 commit.to_string(),
398 ],
399 )); 389 ));
400 } 390 }
401 391
@@ -428,12 +418,9 @@ mod tests {
428 #[test] 418 #[test]
429 fn test_parse_announcement_missing_identifier() { 419 fn test_parse_announcement_missing_identifier() {
430 let keys = create_test_keys(); 420 let keys = create_test_keys();
431 let event = EventBuilder::new( 421 let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repository")
432 Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), 422 .sign_with_keys(&keys)
433 "Test repository", 423 .unwrap();
434 )
435 .sign_with_keys(&keys)
436 .unwrap();
437 424
438 let result = RepositoryAnnouncement::from_event(event); 425 let result = RepositoryAnnouncement::from_event(event);
439 assert!(result.is_err()); 426 assert!(result.is_err());
@@ -539,7 +526,7 @@ mod tests {
539 #[test] 526 #[test]
540 fn test_announcement_maintainers() { 527 fn test_announcement_maintainers() {
541 use nostr_sdk::Tag; 528 use nostr_sdk::Tag;
542 529
543 let keys = create_test_keys(); 530 let keys = create_test_keys();
544 let maintainer_keys = create_test_keys(); 531 let maintainer_keys = create_test_keys();
545 532
@@ -558,13 +545,10 @@ mod tests {
558 // Add maintainer 545 // Add maintainer
559 tags.push(Tag::public_key(maintainer_keys.public_key())); 546 tags.push(Tag::public_key(maintainer_keys.public_key()));
560 547
561 let event = EventBuilder::new( 548 let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repository")
562 Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), 549 .tags(tags)
563 "Test repository", 550 .sign_with_keys(&keys)
564 ) 551 .unwrap();
565 .tags(tags)
566 .sign_with_keys(&keys)
567 .unwrap();
568 552
569 let announcement = RepositoryAnnouncement::from_event(event).unwrap(); 553 let announcement = RepositoryAnnouncement::from_event(event).unwrap();
570 assert_eq!(announcement.maintainers.len(), 1); 554 assert_eq!(announcement.maintainers.len(), 1);
@@ -573,7 +557,7 @@ mod tests {
573 #[test] 557 #[test]
574 fn test_state_with_tags() { 558 fn test_state_with_tags() {
575 use nostr_sdk::Tag; 559 use nostr_sdk::Tag;
576 560
577 let keys = create_test_keys(); 561 let keys = create_test_keys();
578 let mut tags = vec![Tag::custom( 562 let mut tags = vec![Tag::custom(
579 nostr_sdk::TagKind::d(), 563 nostr_sdk::TagKind::d(),