-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
78 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,106 @@ | ||
use anyhow::Result; | ||
use clap::Parser; | ||
use nostr_sdk::{JsonUtil, Kind, RelayPoolNotification}; | ||
use tokio::{pin, task}; | ||
use tracing::{error, info}; | ||
|
||
pub mod config; | ||
pub mod database; | ||
pub mod nwc; | ||
pub mod server; | ||
pub mod services; | ||
|
||
pub mod state; | ||
use axum::Router; | ||
use state::AppState; | ||
use tower_http::services::ServeDir; | ||
|
||
use crate::config::Cli; | ||
use crate::server::run_server; | ||
use crate::state::AppState; | ||
|
||
/// Fedimint Nostr Wallet Connect | ||
/// A nostr wallet connect implementation on top of a multimint client | ||
#[tokio::main] | ||
async fn main() -> Result<()> { | ||
tracing_subscriber::fmt::init(); | ||
dotenv::dotenv().ok(); | ||
|
||
init_logging_and_env()?; | ||
let cli = Cli::parse(); | ||
let state = AppState::new(cli).await?; | ||
|
||
// Connect to the relay pool and broadcast the nwc info event on startup | ||
state.nostr_service.connect().await; | ||
state.nostr_service.broadcast_info_event().await?; | ||
|
||
let server = Router::new().nest_service("/", ServeDir::new("frontend/assets")); | ||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); | ||
|
||
// Spawn a new Tokio task for the server | ||
let server_task = task::spawn(async move { | ||
axum::serve(listener, server).await.unwrap(); | ||
let server_handle = tokio::spawn(async { | ||
match run_server().await { | ||
Ok(_) => info!("Server ran successfully."), | ||
Err(e) => { | ||
error!("Server failed to run: {}", e); | ||
std::process::exit(1); | ||
} | ||
} | ||
}); | ||
|
||
// Wait for the server task to complete if necessary | ||
server_task.await?; | ||
let ctrl_c = tokio::signal::ctrl_c(); | ||
tokio::pin!(ctrl_c); | ||
|
||
// Start the event loop | ||
event_loop(state.clone()).await?; | ||
tokio::select! { | ||
_ = &mut ctrl_c => { | ||
info!("Ctrl+C received. Shutting down..."); | ||
}, | ||
_ = event_loop(state.clone()) => { | ||
info!("Event loop exited unexpectedly."); | ||
}, | ||
_ = server_handle => { | ||
info!("Server task exited unexpectedly."); | ||
} | ||
} | ||
|
||
Ok(()) | ||
shutdown(state).await | ||
} | ||
|
||
/// Event loop that listens for nostr wallet connect events and handles them | ||
async fn event_loop(state: AppState) -> Result<()> { | ||
// Handle ctrl+c to gracefully shutdown the event loop | ||
let ctrl_c = tokio::signal::ctrl_c(); | ||
pin!(ctrl_c); | ||
|
||
let mut notifications = state.nostr_service.notifications(); | ||
info!("Listening for events..."); | ||
loop { | ||
tokio::select! { | ||
_ = &mut ctrl_c => { | ||
info!("Ctrl+C received. Waiting for active requests to complete..."); | ||
state.wait_for_active_requests().await; | ||
info!("All active requests completed."); | ||
break; | ||
}, | ||
notification = notifications.recv() => { | ||
if let Ok(notification) = notification { | ||
if let RelayPoolNotification::Event { event, .. } = notification { | ||
// Only handle nwc events | ||
if event.kind == Kind::WalletConnectRequest | ||
&& event.pubkey == state.nostr_service.user_keys().public_key() | ||
&& event.verify().is_ok() { | ||
info!("Received event: {}", event.as_json()); | ||
state.handle_event(*event).await | ||
} else { | ||
error!("Invalid nwc event: {}", event.as_json()); | ||
} | ||
} else if let RelayPoolNotification::Shutdown = notification { | ||
info!("Relay pool shutdown"); | ||
break; | ||
} else { | ||
error!("Unhandled relay pool notification: {notification:?}"); | ||
} | ||
handle_notification(notification, &state).await?; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
async fn handle_notification(notification: RelayPoolNotification, state: &AppState) -> Result<()> { | ||
match notification { | ||
RelayPoolNotification::Event { event, .. } => { | ||
if event.kind == Kind::WalletConnectRequest | ||
&& event.pubkey == state.nostr_service.user_keys().public_key() | ||
&& event.verify().is_ok() | ||
{ | ||
info!("Received event: {}", event.as_json()); | ||
state.handle_event(*event).await; | ||
} else { | ||
error!("Invalid nwc event: {}", event.as_json()); | ||
} | ||
Ok(()) | ||
} | ||
RelayPoolNotification::Shutdown => { | ||
info!("Relay pool shutdown"); | ||
Err(anyhow::anyhow!("Relay pool shutdown")) | ||
} | ||
_ => { | ||
error!("Unhandled relay pool notification: {notification:?}"); | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
async fn shutdown(state: AppState) -> Result<()> { | ||
info!("Shutting down services and server..."); | ||
state.wait_for_active_requests().await; | ||
info!("All active requests completed."); | ||
state.nostr_service.disconnect().await?; | ||
info!("Services disconnected."); | ||
Ok(()) | ||
} | ||
|
||
fn init_logging_and_env() -> Result<()> { | ||
tracing_subscriber::fmt::init(); | ||
dotenv::dotenv().ok(); | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
use axum::Router; | ||
use tokio::net::TcpListener; | ||
use tower_http::services::ServeDir; | ||
use tracing::error; | ||
|
||
pub async fn run_server() -> Result<(), anyhow::Error> { | ||
let server = Router::new().nest_service("/", ServeDir::new("frontend/assets")); | ||
let listener = TcpListener::bind("0.0.0.0:3000").await?; | ||
axum::serve(listener, server).await.map_err(|e| { | ||
error!("Server failed to run: {}", e); | ||
e | ||
})?; | ||
Ok(()) | ||
} |