Initial commit

This commit is contained in:
Tony Klink 2024-01-12 09:35:31 -06:00
commit 9fe412be11
Signed by: klink
GPG key ID: 85175567C4D19231
58 changed files with 6215 additions and 0 deletions

12
src/relay/handler.rs Normal file
View file

@ -0,0 +1,12 @@
use crate::relay::ws;
use crate::utils::structs::Context;
use std::net::SocketAddr;
use warp::{Rejection, Reply};
pub async fn ws_handler(
ws: warp::ws::Ws,
context: Context,
client_addr: Option<SocketAddr>,
) -> Result<impl Reply, Rejection> {
Ok(ws.on_upgrade(move |socket| ws::client_connection(socket, context, client_addr)))
}

24
src/relay/mod.rs Normal file
View file

@ -0,0 +1,24 @@
mod handler;
mod routes;
mod ws;
use crate::utils::rejection_handler::handle_rejection;
use crate::utils::structs::Context;
use tokio::runtime;
use warp::Filter;
pub fn start(context: Context) {
let rt = runtime::Runtime::new().unwrap();
rt.block_on(async {
log::info!("Starting Relay on wss://127.0.0.1:8080");
let routes = routes::routes(context).recover(handle_rejection);
warp::serve(routes)
// .tls()
// .cert(CERT)
// .key(KEY)
.run(([127, 0, 0, 1], 8080))
.await;
});
}

31
src/relay/routes.rs Normal file
View file

@ -0,0 +1,31 @@
use super::handler;
use crate::utils::filter::with_context;
use crate::utils::structs::Context;
use warp::{Filter, Rejection, Reply};
pub fn routes(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
let cors = warp::cors().allow_any_origin();
static_files().or(index(context)).with(cors)
}
fn index(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
let client_addr = warp::addr::remote();
warp::path::end()
.and(warp::ws())
.and(with_context(context))
.and(client_addr)
.and_then(handler::ws_handler)
}
fn static_files() -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
let mut foo = std::env::current_exe().unwrap();
foo.pop();
let mut www = foo.clone();
www.pop();
www.push(std::path::Path::new("www/static"));
warp::get().and(warp::fs::dir(www))
}

264
src/relay/ws.rs Normal file
View file

@ -0,0 +1,264 @@
use crate::{
bussy::channels,
utils::structs::{Client, Context, Subscription},
};
use futures_util::StreamExt;
use nostr::{ClientMessage, Event, Filter, JsonUtil, SubscriptionId};
use serde_json::from_str;
use std::net::SocketAddr;
use tokio::sync::mpsc;
use tokio_stream::wrappers::UnboundedReceiverStream;
use warp::ws::{Message, WebSocket};
use futures_util::SinkExt;
pub async fn client_connection(ws: WebSocket, context: Context, client_addr: Option<SocketAddr>) {
let (mut ws_sender, mut ws_receiver) = ws.split();
let (client_sender, client_receiver) = mpsc::unbounded_channel();
let mut client_receiver = UnboundedReceiverStream::new(client_receiver);
// Create and Add to the Context new Client and set its sender
let ip = client_addr.unwrap().ip().to_string();
let mut client = Client::new(ip);
client.client_connection = Some(client_sender);
let mut subscriber = context.pubsub.subscribe(channels::MSG_RELAY).await;
loop {
tokio::select! {
Ok(message) = subscriber.recv() => {
match message.content {
crate::bussy::Command::PipelineResRelayMessageOk(client_id, relay_message) => {
if client.client_id == client_id {
if let Some(sender) = &client.client_connection {
if !sender.is_closed() {
log::info!("[Relay] sending back the status of the processed event: {}", relay_message.as_json());
sender.send(Ok(Message::text(relay_message.as_json()))).unwrap();
}
}
}
},
crate::bussy::Command::PipelineResStreamOutEvent(event) => {
// if client.client_id == client_id {
if let Some(sender) = &client.client_connection {
if let Some(relay_message) = get_relay_message(&client, event) {
log::info!("[Relay] sending processed event to subscribed client: {}", relay_message.as_json());
if !sender.is_closed() {sender.send(Ok(Message::text(relay_message.as_json()))).unwrap()};
}
}
// }
}
crate::bussy::Command::DbResRelayMessage(client_id, events) => {
if client.client_id == client_id {
if let Some(sender) = &client.client_connection {
if !sender.is_closed() {
for event in events {
sender.send(Ok(Message::text(event))).unwrap();
}
}
}
}
}
crate::bussy::Command::DbResOkWithStatus(status) => {
if let Some(sender) = &client.client_connection {
sender.send(Ok(Message::text(status))).unwrap();
}
},
_ => ()
}
},
Some(message) = client_receiver.next() => {
match message {
Ok(message) => {
// ws_sender
// .send(message)
// .unwrap_or_else(|e| {
// log::error!("websocket send error: {}", e);
// })
// .await;
match ws_sender.send(message).await {
Ok(_) => (),
Err(e) => {
log::error!("websocket send error: {}", e);
break;
}
}
}
Err(e) => {
log::error!("websocket send error: {}", e);
break;
}
}
},
Some(result) = ws_receiver.next() => {
let msg = match result {
Ok(msg) => msg,
Err(e) => {
log::error!(
"error receiving ws message for id: {}: {}",
client.client_id.clone(),
e
);
break;
}
};
socket_on_message(&context, &mut client, msg).await;
}
}
}
// Handle proper disconnects
socket_on_close(&client).await;
}
/// Checking if client with id needs the event
fn get_relay_message(client: &Client, event: Box<Event>) -> Option<nostr::RelayMessage> {
let mut id = &"".to_string();
log::info!(
"Checking if client with id {} needs the event",
client.client_id
);
if client.subscriptions.iter().any(|(sub_id, sub)| {
if sub.interested_in_event(&event) {
id = sub_id;
return true;
}
false
}) {
return Some(nostr::RelayMessage::Event {
subscription_id: nostr::SubscriptionId::new(id),
event,
});
}
None
}
async fn socket_on_close(client: &Client) {
// clients.write().await.remove(id);
log::info!("{} disconnected", client.client_id);
}
async fn socket_on_message(context: &Context, client: &mut Client, msg: Message) {
let message = match msg.to_str() {
Ok(raw_message) => raw_message,
Err(_) => return,
};
if message == "ping" || message == "ping\n" {
return;
}
let client_message: ClientMessage = match from_str(message) {
Ok(parsed_message) => parsed_message,
Err(e) => {
log::error!("error while parsing client message request: {}", e);
let response = nostr::RelayMessage::new_notice("Invalid message");
let message = Message::text(response.as_json());
send(client, message);
return;
}
};
log::info!(
"[client {} - {}] message: {}",
client.ip(),
client.client_id,
client_message.as_json()
);
handle_msg(context, client, client_message).await;
}
fn send(client: &Client, message: Message) {
if let Some(sender) = &client.client_connection {
if !sender.is_closed() {
sender.send(Ok(message)).unwrap();
}
}
}
async fn handle_msg(context: &Context, client: &mut Client, client_message: ClientMessage) {
match client_message {
ClientMessage::Event(event) => handle_event(context, client, event).await,
ClientMessage::Req {
subscription_id,
filters,
} => handle_req(context, client, subscription_id, filters).await,
ClientMessage::Count {
subscription_id,
filters,
} => handle_count(client, subscription_id, filters).await,
ClientMessage::Close(subscription_id) => handle_close(client, subscription_id).await,
ClientMessage::Auth(event) => handle_auth(client, event).await,
_ => (),
}
}
async fn handle_event(context: &Context, client: &Client, event: Box<Event>) {
log::debug!("handle_event is processing new event");
context
.pubsub
.publish(
channels::MSG_PIPELINE,
crate::bussy::Message {
source: channels::MSG_RELAY,
content: crate::bussy::Command::PipelineReqEvent(client.client_id, event),
},
)
.await;
}
async fn handle_req(
context: &Context,
client: &mut Client,
subscription_id: SubscriptionId,
filters: Vec<Filter>,
) {
let subscription = Subscription::new(subscription_id.clone(), filters);
let needs_historical_events = subscription.needs_historical_events();
client.subscribe(subscription.clone()).unwrap();
if needs_historical_events {
context
.pubsub
.publish(
channels::MSG_NOOSE,
crate::bussy::Message {
source: channels::MSG_RELAY,
content: crate::bussy::Command::DbReqFindEvent(client.client_id, subscription),
},
)
.await
}
}
async fn handle_count(client: &Client, subscription_id: SubscriptionId, filters: Vec<Filter>) {
// context.pubsub.send(new nostr event) then handle possible errors
let subscription = Subscription::new(subscription_id, filters);
let message = Message::text("COUNT not implemented");
send(client, message);
}
async fn handle_close(client: &mut Client, subscription_id: SubscriptionId) {
// context.pubsub.send(new nostr event) then handle possible errors
client.unsubscribe(subscription_id);
// let message = Message::text("CLOSE not implemented");
// send(client, message);
}
async fn handle_auth(client: &Client, event: Box<Event>) {
let message = Message::text("AUTH not implemented");
send(client, message);
}