upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/http
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-03 16:15:55 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-03 16:15:55 +0000
commitdd1b44132199aa72c2b699e1160fbe6b885f0ef6 (patch)
treea4f3ea50770f8ef22d5f5140e79bbfddec563b0d /src/http
parent2591dbf7498e2300a42928ac9fbec454732e66f0 (diff)
landing page: display relay inforamtion document
Diffstat (limited to 'src/http')
-rw-r--r--src/http/landing.rs133
-rw-r--r--src/http/nip11.rs8
2 files changed, 133 insertions, 8 deletions
diff --git a/src/http/landing.rs b/src/http/landing.rs
index c76a7e6..8ab4a68 100644
--- a/src/http/landing.rs
+++ b/src/http/landing.rs
@@ -398,7 +398,7 @@ fn generate_nip_cards(nip11: &RelayInformationDocument) -> String {
398 if let Some(meta) = metadata.get(nip) { 398 if let Some(meta) = metadata.get(nip) {
399 html.push_str(&format!( 399 html.push_str(&format!(
400 r#"<div class="card"> 400 r#"<div class="card">
401 <div class="card-title"><span class="badge">NIP-{:02}</span> {}</div> 401 <div class="card-title"><span class="badge badge-nip">NIP-{:02}</span> {}</div>
402 <div class="card-desc">{}</div> 402 <div class="card-desc">{}</div>
403 </div>"#, 403 </div>"#,
404 nip, meta.title, meta.description 404 nip, meta.title, meta.description
@@ -407,7 +407,7 @@ fn generate_nip_cards(nip11: &RelayInformationDocument) -> String {
407 // Fallback for unknown NIPs - still show them 407 // Fallback for unknown NIPs - still show them
408 html.push_str(&format!( 408 html.push_str(&format!(
409 r#"<div class="card"> 409 r#"<div class="card">
410 <div class="card-title"><span class="badge">NIP-{:02}</span></div> 410 <div class="card-title"><span class="badge badge-nip">NIP-{:02}</span></div>
411 </div>"#, 411 </div>"#,
412 nip 412 nip
413 )); 413 ));
@@ -418,6 +418,129 @@ fn generate_nip_cards(nip11: &RelayInformationDocument) -> String {
418 html 418 html
419} 419}
420 420
421/// Escape HTML special characters
422fn escape_html(s: &str) -> String {
423 s.replace('&', "&amp;")
424 .replace('<', "&lt;")
425 .replace('>', "&gt;")
426 .replace('"', "&quot;")
427 .replace('\'', "&#39;")
428}
429
430/// Generate table rows for NIP-11 relay information
431fn generate_relay_info_rows(nip11: &RelayInformationDocument) -> String {
432 let mut html = String::new();
433
434 // Follow NIP-11 document order:
435 // name
436 html.push_str(&format!(
437 r#"<tr><th>name</th><td>{}</td></tr>"#,
438 escape_html(&nip11.name)
439 ));
440 html.push('\n');
441
442 // description
443 html.push_str(&format!(
444 r#"<tr><th>description</th><td>{}</td></tr>"#,
445 escape_html(&nip11.description)
446 ));
447 html.push('\n');
448
449 // pubkey (if present)
450 if let Some(ref pubkey) = nip11.pubkey {
451 html.push_str(&format!(
452 r#"<tr><th>pubkey</th><td><code>{}</code></td></tr>"#,
453 escape_html(pubkey)
454 ));
455 html.push('\n');
456 }
457
458 // contact (if present)
459 if let Some(ref contact) = nip11.contact {
460 html.push_str(&format!(
461 r#"<tr><th>contact</th><td>{}</td></tr>"#,
462 escape_html(contact)
463 ));
464 html.push('\n');
465 }
466
467 // supported_nips
468 let nips_str = nip11
469 .supported_nips
470 .iter()
471 .map(|n| n.to_string())
472 .collect::<Vec<_>>()
473 .join(", ");
474 html.push_str(&format!(
475 r#"<tr><th>supported_nips</th><td>{}</td></tr>"#,
476 nips_str
477 ));
478 html.push('\n');
479
480 // software
481 html.push_str(&format!(
482 r#"<tr><th>software</th><td><a href="{}">{}</a></td></tr>"#,
483 escape_html(&nip11.software),
484 escape_html(&nip11.software)
485 ));
486 html.push('\n');
487
488 // version
489 html.push_str(&format!(
490 r#"<tr><th>version</th><td>{}</td></tr>"#,
491 escape_html(&nip11.version)
492 ));
493 html.push('\n');
494
495 // icon (if present)
496 if let Some(ref icon) = nip11.icon {
497 html.push_str(&format!(
498 r#"<tr><th>icon</th><td><a href="{}">{}</a></td></tr>"#,
499 escape_html(icon),
500 escape_html(icon)
501 ));
502 html.push('\n');
503 }
504
505 // GRASP-01 Extensions:
506 // supported_grasps
507 let grasps_str = nip11.supported_grasps.join(", ");
508 html.push_str(&format!(
509 r#"<tr><th>supported_grasps</th><td>{}</td></tr>"#,
510 escape_html(&grasps_str)
511 ));
512 html.push('\n');
513
514 // repo_acceptance_criteria
515 html.push_str(&format!(
516 r#"<tr><th>repo_acceptance_criteria</th><td>{}</td></tr>"#,
517 escape_html(&nip11.repo_acceptance_criteria)
518 ));
519 html.push('\n');
520
521 // curation (if present)
522 if let Some(ref curation) = nip11.curation {
523 html.push_str(&format!(
524 r#"<tr><th>curation</th><td>{}</td></tr>"#,
525 escape_html(curation)
526 ));
527 html.push('\n');
528 }
529
530 html
531}
532
533/// Generate the relay icon HTML for the header
534fn generate_relay_icon_html(nip11: &RelayInformationDocument) -> String {
535 match &nip11.icon {
536 Some(icon_url) => format!(
537 r#"<img src="{}" alt="Relay Icon" class="relay-icon">"#,
538 escape_html(icon_url)
539 ),
540 None => String::new(),
541 }
542}
543
421/// Generate the HTML landing page 544/// Generate the HTML landing page
422pub fn get_html(config: &Config) -> String { 545pub fn get_html(config: &Config) -> String {
423 // Get NIP-11 document for supported NIPs and GRASPs 546 // Get NIP-11 document for supported NIPs and GRASPs
@@ -430,6 +553,8 @@ pub fn get_html(config: &Config) -> String {
430 let hero_tags = generate_hero_tags(&nip11); 553 let hero_tags = generate_hero_tags(&nip11);
431 let grasp_cards = generate_grasp_cards(&nip11); 554 let grasp_cards = generate_grasp_cards(&nip11);
432 let nip_cards = generate_nip_cards(&nip11); 555 let nip_cards = generate_nip_cards(&nip11);
556 let relay_info_rows = generate_relay_info_rows(&nip11);
557 let relay_icon = generate_relay_icon_html(&nip11);
433 558
434 format!( 559 format!(
435 include_str!("../../templates/landing.html"), 560 include_str!("../../templates/landing.html"),
@@ -437,12 +562,16 @@ pub fn get_html(config: &Config) -> String {
437 relay_name = config.relay_name(), 562 relay_name = config.relay_name(),
438 relay_description = config.relay_description, 563 relay_description = config.relay_description,
439 version = get_version(), 564 version = get_version(),
565 nip11_version = escape_html(&nip11.version),
440 curation = curation, 566 curation = curation,
567 repo_acceptance_criteria = escape_html(&nip11.repo_acceptance_criteria),
441 theme_toggle = get_theme_toggle_html(), 568 theme_toggle = get_theme_toggle_html(),
442 theme_script = get_theme_script(), 569 theme_script = get_theme_script(),
443 hero_tags = hero_tags, 570 hero_tags = hero_tags,
444 grasp_cards = grasp_cards, 571 grasp_cards = grasp_cards,
445 nip_cards = nip_cards, 572 nip_cards = nip_cards,
573 relay_info_rows = relay_info_rows,
574 relay_icon = relay_icon,
446 ) 575 )
447} 576}
448 577
diff --git a/src/http/nip11.rs b/src/http/nip11.rs
index 2a37385..1ed80de 100644
--- a/src/http/nip11.rs
+++ b/src/http/nip11.rs
@@ -76,11 +76,7 @@ impl RelayInformationDocument {
76 76
77 // GRASP-01 Extensions 77 // GRASP-01 Extensions
78 supported_grasps: vec!["GRASP-01".to_string()], 78 supported_grasps: vec!["GRASP-01".to_string()],
79 repo_acceptance_criteria: format!( 79 repo_acceptance_criteria: "None".to_string(),
80 "Repositories must list this relay ({}) in both 'clone' and 'relays' tags of kind 30617 announcements. \
81 All other events must reference accepted repositories or accepted events.",
82 config.domain
83 ),
84 curation: None, // Not a curated relay - only SPAM prevention via GRASP-01 policy 80 curation: None, // Not a curated relay - only SPAM prevention via GRASP-01 policy
85 } 81 }
86 } 82 }
@@ -118,7 +114,7 @@ mod tests {
118 assert!(doc.supported_nips.contains(&34)); 114 assert!(doc.supported_nips.contains(&34));
119 assert!(doc.supported_nips.contains(&77)); 115 assert!(doc.supported_nips.contains(&77));
120 assert_eq!(doc.supported_grasps, vec!["GRASP-01"]); 116 assert_eq!(doc.supported_grasps, vec!["GRASP-01"]);
121 assert!(doc.repo_acceptance_criteria.contains("relay.example.com")); 117 assert!(doc.repo_acceptance_criteria.contains("None"));
122 assert!(doc.curation.is_none()); 118 assert!(doc.curation.is_none());
123 assert_eq!( 119 assert_eq!(
124 doc.icon, 120 doc.icon,