Initial commit
This commit is contained in:
commit
9fe412be11
58 changed files with 6215 additions and 0 deletions
12
src/relay/handler.rs
Normal file
12
src/relay/handler.rs
Normal 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
24
src/relay/mod.rs
Normal 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
31
src/relay/routes.rs
Normal 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
264
src/relay/ws.rs
Normal 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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue