1+ //! Multi-identity server builder for modern daemon architecture
2+ //!
3+ //! This module provides the `serve_all()` builder that automatically discovers
4+ //! and serves all configured identities and protocols from FASTN_HOME.
5+
6+ use std:: path:: PathBuf ;
7+ use std:: collections:: HashMap ;
8+ use std:: future:: Future ;
9+ use std:: pin:: Pin ;
10+
11+ /// Async callback type for request/response protocols
12+ pub type RequestCallback = fn (
13+ & str , // identity
14+ & str , // bind_alias
15+ & PathBuf , // protocol_dir
16+ serde_json:: Value , // request
17+ ) -> Pin < Box < dyn Future < Output = Result < serde_json:: Value , Box < dyn std:: error:: Error + Send + Sync > > > + Send > > ;
18+
19+ /// Async callback type for streaming protocols
20+ pub type StreamCallback = fn (
21+ & str , // identity
22+ & str , // bind_alias
23+ & PathBuf , // protocol_dir
24+ serde_json:: Value , // initial_data
25+ ) -> Pin < Box < dyn Future < Output = Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > > > + Send > > ;
26+
27+ /// Multi-identity server builder that discovers and serves all configured protocols
28+ pub struct ServeAllBuilder {
29+ fastn_home : PathBuf ,
30+ request_callbacks : HashMap < String , RequestCallback > ,
31+ stream_callbacks : HashMap < String , StreamCallback > ,
32+ }
33+
34+ impl ServeAllBuilder {
35+ /// Register a request/response callback for a protocol
36+ pub fn handle_requests ( mut self , protocol_name : & str , callback : RequestCallback ) -> Self {
37+ self . request_callbacks . insert ( protocol_name. to_string ( ) , callback) ;
38+ self
39+ }
40+
41+ /// Register a streaming callback for a protocol
42+ pub fn handle_streams ( mut self , protocol_name : & str , callback : StreamCallback ) -> Self {
43+ self . stream_callbacks . insert ( protocol_name. to_string ( ) , callback) ;
44+ self
45+ }
46+
47+ /// Start serving all configured identities and protocols
48+ pub async fn serve ( self ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
49+ println ! ( "🚀 Starting multi-identity P2P server" ) ;
50+ println ! ( "📁 FASTN_HOME: {}" , self . fastn_home. display( ) ) ;
51+
52+ // Load all identity configurations using daemon utilities
53+ let identity_configs = super :: daemon:: load_all_identities ( & self . fastn_home ) . await ?;
54+
55+ let online_identities: Vec < _ > = identity_configs. into_iter ( )
56+ . filter ( |id| id. online )
57+ . collect ( ) ;
58+
59+ if online_identities. is_empty ( ) {
60+ return Err ( "No online identities found. Set identities online with: fastn-p2p identity-online <name>" . into ( ) ) ;
61+ }
62+
63+ println ! ( "🔑 Found {} online identities" , online_identities. len( ) ) ;
64+
65+ // Start P2P listeners for each identity/protocol combination
66+ for identity_config in online_identities {
67+ println ! ( "🎧 Starting services for identity: {}" , identity_config. alias) ;
68+
69+ for protocol_binding in & identity_config. protocols {
70+ let protocol_dir = protocol_binding. config_path . clone ( ) ;
71+
72+ println ! ( " 📡 {} {} → {}" ,
73+ protocol_binding. protocol,
74+ protocol_binding. bind_alias,
75+ protocol_dir. display( ) ) ;
76+
77+ // Check if we have a handler for this protocol
78+ if let Some ( callback) = self . request_callbacks . get ( & protocol_binding. protocol ) {
79+ println ! ( " 🔄 Starting request handler for {}" , protocol_binding. protocol) ;
80+
81+ // TODO: Start actual P2P listener and route requests to callback
82+ // For now, just log that we would start it
83+ let identity = identity_config. alias . clone ( ) ;
84+ let bind_alias = protocol_binding. bind_alias . clone ( ) ;
85+ let protocol = protocol_binding. protocol . clone ( ) ;
86+ let protocol_dir_clone = protocol_dir. clone ( ) ;
87+
88+ tokio:: spawn ( async move {
89+ println ! ( "🎧 Would start P2P listener for {} {} ({})" , protocol, bind_alias, identity) ;
90+ println ! ( " Working dir: {}" , protocol_dir_clone. display( ) ) ;
91+ // TODO: Start fastn_p2p::listen() and route to callback
92+ } ) ;
93+ }
94+
95+ if let Some ( callback) = self . stream_callbacks . get ( & protocol_binding. protocol ) {
96+ println ! ( " 🌊 Starting stream handler for {}" , protocol_binding. protocol) ;
97+ // TODO: Similar to request handler but for streaming
98+ }
99+ }
100+ }
101+
102+ println ! ( "🎯 Multi-identity server ready (TODO: implement actual P2P listening)" ) ;
103+
104+ // Keep server running
105+ loop {
106+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_secs ( 1 ) ) . await ;
107+ }
108+ }
109+ }
110+
111+ /// Create a new multi-identity server builder
112+ pub fn serve_all ( ) -> ServeAllBuilder {
113+ let fastn_home = std:: env:: var ( "FASTN_HOME" )
114+ . map ( PathBuf :: from)
115+ . unwrap_or_else ( |_| {
116+ let home = std:: env:: var ( "HOME" ) . unwrap_or ( "/tmp" . to_string ( ) ) ;
117+ PathBuf :: from ( home) . join ( ".fastn" )
118+ } ) ;
119+
120+ ServeAllBuilder {
121+ fastn_home,
122+ request_callbacks : HashMap :: new ( ) ,
123+ stream_callbacks : HashMap :: new ( ) ,
124+ }
125+ }
126+
127+ /// Echo request handler callback
128+ pub fn echo_request_handler (
129+ identity : & str ,
130+ bind_alias : & str ,
131+ protocol_dir : & PathBuf ,
132+ request : serde_json:: Value ,
133+ ) -> Pin < Box < dyn Future < Output = Result < serde_json:: Value , Box < dyn std:: error:: Error + Send + Sync > > > + Send > > {
134+ let identity = identity. to_string ( ) ;
135+ let bind_alias = bind_alias. to_string ( ) ;
136+ let protocol_dir = protocol_dir. clone ( ) ;
137+
138+ Box :: pin ( async move {
139+ println ! ( "💬 Echo handler called:" ) ;
140+ println ! ( " Identity: {}" , identity) ;
141+ println ! ( " Bind alias: {}" , bind_alias) ;
142+ println ! ( " Protocol dir: {}" , protocol_dir. display( ) ) ;
143+
144+ // Parse request
145+ let message = request. get ( "message" )
146+ . and_then ( |v| v. as_str ( ) )
147+ . unwrap_or ( "(no message)" ) ;
148+
149+ if message. is_empty ( ) {
150+ return Err ( "Message cannot be empty" . into ( ) ) ;
151+ }
152+
153+ println ! ( " Message: '{}'" , message) ;
154+
155+ // Create response
156+ let response = serde_json:: json!( {
157+ "echoed" : format!( "Echo from {}: {}" , identity, message)
158+ } ) ;
159+
160+ println ! ( "📤 Echo response: {}" , response) ;
161+ Ok ( response)
162+ } )
163+ }
0 commit comments