From c07954f44f4c03cc17d4a83b144667cbcbb226cf Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Tue, 2 Dec 2025 17:33:39 +0000 Subject: [PATCH] nip11 add icon --- src/http/mod.rs | 17 +++++++++++++++++ src/http/nip11.rs | 10 ++++++++++ static/icon.png | Bin 0 -> 8880 bytes 3 files changed, 27 insertions(+) create mode 100644 static/icon.png diff --git a/src/http/mod.rs b/src/http/mod.rs index c47c374..5cf8dbe 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -32,6 +32,9 @@ const CORS_ALLOW_ORIGIN: &str = "*"; const CORS_ALLOW_METHODS: &str = "GET, POST"; const CORS_ALLOW_HEADERS: &str = "Content-Type"; +/// Embedded icon image (Grasp logo) +const ICON_PNG: &[u8] = include_bytes!("../../static/icon.png"); + /// Extract npub and identifier from a repository URL path (no git subpath required) /// /// Parses paths like `//.git` (for repository webpage/404) @@ -359,6 +362,20 @@ impl Service> for HttpService { } } + // Serve static icon at /icon.png + if path == "/icon.png" { + return Box::pin(async move { + Ok( + add_cors_headers(Response::builder().header("server", "ngit-grasp")) + .status(200) + .header("content-type", "image/png") + .header("cache-control", "public, max-age=86400") + .body(Full::new(Bytes::from_static(ICON_PNG))) + .unwrap(), + ) + }); + } + // Only serve landing page for root path "/", 404 for everything else let config = self.config.clone(); Box::pin(async move { diff --git a/src/http/nip11.rs b/src/http/nip11.rs index 901b676..ecb9769 100644 --- a/src/http/nip11.rs +++ b/src/http/nip11.rs @@ -34,6 +34,10 @@ pub struct RelayInformationDocument { /// Software version pub version: String, + /// Relay icon URL (NIP-11 optional field) + #[serde(skip_serializing_if = "Option::is_none")] + pub icon: Option, + // GRASP-01 Extensions (lines 11-14 of GRASP-01 spec) /// List of supported GRASPs (e.g., ["GRASP-01"]) /// Required by GRASP-01 specification line 12 @@ -67,6 +71,7 @@ impl RelayInformationDocument { Some(commit) => format!("{}-{}", env!("CARGO_PKG_VERSION"), commit), None => env!("CARGO_PKG_VERSION").to_string(), }, + icon: Some(format!("https://{}/icon.png", config.domain)), // GRASP-01 Extensions supported_grasps: vec!["GRASP-01".to_string()], @@ -113,6 +118,10 @@ mod tests { assert_eq!(doc.supported_grasps, vec!["GRASP-01"]); assert!(doc.repo_acceptance_criteria.contains("relay.example.com")); assert!(doc.curation.is_none()); + assert_eq!( + doc.icon, + Some("https://relay.example.com/icon.png".to_string()) + ); } #[test] @@ -143,5 +152,6 @@ mod tests { let parsed: serde_json::Value = serde_json::from_str(&json).expect("Invalid JSON"); assert_eq!(parsed["name"], "Test Relay"); assert_eq!(parsed["supported_grasps"][0], "GRASP-01"); + assert_eq!(parsed["icon"], "https://relay.example.com/icon.png"); } } diff --git a/static/icon.png b/static/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1eef256fc3f7519ca11db3e4c9f5f4575c0e3da4 GIT binary patch literal 8880 zcmeHtc{tSH+y9v{BFes%WrVa)XrWSO5+y02ETNewWC{7$mlpt&^xOm~rR=(|g z0D!G$4fIR_K*Cof;N1iti+ZhRpD4h7J>MF4o=NcyxQ;LX5=;3X)bIqgc z)v011dALVsyXil|CN-)8$4A_nst&@^3Ka<9mWV5hKm* z^iz%fI(l{-op^h;0%=r7+XJw^C;%qnpy{$A0A!qklm5p50HVNV0KV)%0B}qR0PKVR z75eW(K-_#})gaGqW!7~0r!{eN)inK#`;XmNFT1apKOTE90i*<5-^Bdd)tUrj%>L1N zwR3A@-P}%aNn#=*IICVu=B#HC@*Z@zli-RXz$&H%y-N^P5#^-4vi@@iywL~55xXqH ze3t3*iHj-nyjW=zFmRqbkyUg17}cCp^V8quFfTZ|8N_|*uvh74QM=V zeYoTm5kRE({efTU0Tl_S&a}Mh*-8vrL=cshOPsi7X`JAPq3r6HY z-JOOJ0(0%_&K*F)7!bL`-8ffwakjzIIY4a5Y#~>=ah6(jiT#L+=>Ul9`;rMu&c8H; zk?qg-VQ(P8C6R(3mNUy2XD}tb>jK086v!R%8`4_4;j!53Lkk4^L_o$Jr#qseGSc7n z2~7zD{B_S+v<17z<~x_niq%PRB#2fmUhielj-5eVd;oxAWGBIua}Zq()O8Tx^x~bC zWw!`f@L@A$C#W;96VVQOReFBcvAG0Zdr^Q_B9-|%#14x}J%O6x<%D~#+PgrV9|}mF zoKs_LGrYU|Bp+o5sH@9-6!T=-Bkia;QJ5E~m!66h-z26D-f`9cfwD=RoKm}a!cZP^ zixD6q8n2JG2KW`5{cVf;M37mR^VkAT!Qrs&oF?IT!d-wrf&^Qdl@6zI2}NN10KMl%2~~2FBB%ow zQumLpF5DBI3Cu|FlDrF~A_34YT}des{bHV7XqAi5n{ZJuhOC3A+J`(5QFrV0W4g)^~5#Z2D;Tgy^jgkyX0 zEFV~a@CcYp66QyB6+E@&eeA_}>Vr_A=MF7RyI|4yW^BNgq-Ov;OA=qtJ5F_@wn=ZJ z%Ba*y@Pg7%mY}5-X{<3(oTuI#mLFF2qvOejoMk=F!B2^Nr3eJ*5^46m|bEtMQWHoGlA}kOl8O=-`X*ZqI0C*<9JX=O)_7IK+eY-mzUQDc7uS7jT@ zIyh{c3W4nydiLGF(NTRzxgn=;oP{oST}vm}&ycmMQ#32~O~izZ?QV&t9JeBj`TpEH zI@Hgt+#~?vMXX!g3ym@eE9iuTcJkW;$u$*hTCBL|ca+hnfjo(6-^rn;7jdW!`wd(x z(E+OCvrWHOgtQN-@ALsl(N5FPD#~%sccupi-^jG?V{meo28fnRO;c+O32Mjic<_wP zPnQ&vD!*B8Z-mhUI8VJSLRWi8xgEK;cco@X_1J7~r3h-s6+>zr##AcKjySrtdM%C3 z%|CNv@82PcAZ9l{^@K~Sl-yBL%e?ig-=~qf&Ek8S-(Gj2p2-G6b*uYKl#mDGiD_|= zRmM#uXp6KXAB?Ds#;~dq(re!??fX*{es$9W^ydmi^${q~>n%Kcpo_af11sac*!3~= zrZ8~&%6|4_*h+;(^A8-hBk$1jykhh?s)rT6SzVco2_*Coy>fng3*F-VasWXbYq+~I zbndA3`n}vm&L5WP`5*zo6R)?2Q#8Ar35va^u*~9NUxA_yzNGByx2B~Fh9uclvm@wY z3)doP?wG^7I6{uIy9kBB>RZl(jBTEB&)uZm)*^3qbcsfCkYaP8&ipfR~K;^VZL;Zy-5=(f&q!|kA-I-D}l@cxem|F6|z zUIaba(Y5&?e}Pqrdi@OSCGsq$-Ta^hqokXb=Go?B`&x74(~lJygSg2@BFOl1s2kbqxYB{PXxZVq?0 zX&KJCI4F7j$$JwSsq;nn4iu134z#8v={Z*}-H__z`8Wn^GVhUUD*}HWRGkUCW{(7y zZ%&_c>+R=h^82Ebq%Q<=Pg~<8cIsk(Z@T_|htQ6}WP9#>5s<5}msPQJ<58xZyx01m zB+lm)@f`}BmUIiIk`pU?)QVU07U#m2KX^oey4)*H)6(ZdCN_eMA;63EF&?Kbe^|T# z@E(1PuaQ>kgV7E%VS=)I)cFv^lW=&K%wDsup}L;4fis0eSa{H>Ct2n>O`nT;Bba># z3Btnd$bZWPu&jjy+Sx^FW-nmG5e7Y)Zo$d<5`8?5MddGKk)R*OC9}l$!5%U^PUzMP zyr9+1e6j@A2yB8?(#oF!L~?KIH|eu$TK;!Rkdq;FyH(die!5gzYtityK&4M({M4_+D7#PBDa& zNWRm%HMEdsKNBVlj=d~W`@Ybh3YYb)0hlmY-tfjg#)0#FJn@gfmk5bIMw(96*Yrs^ zA6FDnpkYY|PL%#eP+1g#AIdCpFJx9M-IH<9LLI0SG?)CsgTU8ke#KeinjIyOI*g20 zV%7I`>lQZy(I@XWs&|j(xI>JGc!8)`JfUmDoL|+iD+MKx+2=q~QfoY!R9i}Foe3Mh zj};Sk3@$DWM%LZEp1Ltg+YNWL3{~K6U@_f|#g4_O=36V&Wyn#|zip(5fgHJHM2& z1V?Jd(N7>|_3by@&@z==H5aOak=U2ZKV^u$4Uzg;m@T_p)(3OBzi3vu!Fs#F8&fmT zN214_zXqi2J{~e=`N9N=4a=NR`{peJF$xk!4jqxV{_FM!GcF^s_IA@4vkqFxty{)> zm&1-7API)dJP#mBY=)G2s$X^J7U%2}_C;IL@&^+!KjvFqdld10Ll@zx7YA;P0T!{b z8BR3dQG+$=CbT%bhe7@F`wRqH5su*`X{3!Jo^E){DdtWuyE)CjcHJ5Mdw0e@B$g|) z-lrp&4e_fvP$Bp*)sQ>XO$cl+op6x$d~Xc()lRJsCNXD_Z%zV({pRc_)xroEyEdTh zi+d65@f+!9fek@oZ%6%L#BQwDixb=Wc>1}Uje&tG6u-i18Y%gT?yH!PMwa=N@y9&_ zaXlT}OFf!v9}lS>L-`qFZy~X!o^u3Y7JImrHX`_~tXW8?8r(vS>;y^`;US!kZdx+N z$oMW%|Bb-IPqh6o{$Wjd(EBe3do3UkMrLB0_76qk?+auXo(BfINo~$6%vd+V3iDcD zW|ZSh_vWVkL|@d15>WbQwaoPLaj`cm_jL5iGy0Z*v1(QP9v4yYIO2^>`KlU;O*^}M z%M*XxgvVoc$@H?Svl!QyB2e~&n{%);w#4
  • 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