/* * This whole module contains functions that are optionally built for those * (((weirdos))) that don't want rtc capabilities. * * General configuration things * RTC server should run on the same system * * Features TODO: make this fully module so that rtc & api can run on seperate * servers if need be. This can be configured with .env most likely and the rtc * private api can probably be a really simple auth'd REST API, * prolly don't even need much in the way of authentication as long as the API key * is sufficiently large & securely transferred on both parties behalf */ use std::time::{SystemTime, UNIX_EPOCH}; use mysql_async::Pool; use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::{Error, Message}; use futures::{StreamExt, SinkExt}; use serde::Serialize; use serde_json::json; use jsonwebtoken::{ Header, Algorithm, EncodingKey }; use url::Url; lazy_static! { static ref HMAC_SECRET: Vec = { let path = match std::env::var("WSS_HMAC_PATH") { Ok(p) => p, Err(_) => "wss-hmac.secret".into() }; std::fs::read(path).expect("Couldn't get HMAC secret") }; static ref WSS_KEY: EncodingKey = { EncodingKey::from_secret(&HMAC_SECRET) }; } macro_rules! event { ($type:expr, $payload:expr) => { json!({"type": $type, $type: $payload}) } } #[derive(Serialize)] struct Claim { nbf: i64 } fn make_url() -> Url { let claim = Claim { nbf: SystemTime::now() .duration_since(UNIX_EPOCH).unwrap() .as_secs() as i64 }; let header = Header::new(Algorithm::HS512); let jwt = jsonwebtoken::encode(&header, &claim, &WSS_KEY).unwrap(); let base = "ws://localhost:5648/jwt"; let mut url = Url::parse(base).unwrap(); url.query_pairs_mut().append_pair("jwt", jwt.as_str()); url } async fn notify

(event_name: &str, payload: P) -> Result<(), Error> where P: Serialize { // Flow: Connect -> Pick out stream -> Send Data over stream // The stream/connection is destroyed by the end of this call let (ws, _) = connect_async(make_url()).await?; let (mut write, _) = ws.split(); let event = event!(event_name, &payload); let msg = event.to_string(); write.send(Message::text(msg)).await?; Ok(()) } pub async fn new_message(p: &Pool, message: db::Message) { let uname = match db::Member::get(p, message.author_id).await { Ok(response) => { match response { db::Response::Row(user) => user.name, _ => String::new() } }, Err(e) => { eprintln!("[HTTP-RTC] Couldn't fetch username {}", e); String::new() } }; let message = db::UserMessage { id: message.id, time: message.time, content: message.content, content_type: message.content_type, author_id: message.author_id, channel_id: message.channel_id, name: uname, }; if let Err(e) = notify("new-message", message).await { eprintln!("[API-RTC] Unable to connect to RTC server: {}", e); } } pub async fn delete_channel(id: u64) { #[derive(Serialize)] struct DeletedChannel { id: u64 } let channel = DeletedChannel { id }; if let Err(e) = notify("delete-channel", channel).await { eprintln!("[API-RTC] Unable to connect to RTC server: {}", e); } } pub async fn create_channel(channel: db::Channel) { if let Err(e) = notify("create-channel", channel).await { eprintln!("[API-RTC] Unable to connect to RTC server: {}", e); } } pub async fn update_nickname<'s>(id: u64, name: &'s str) { #[derive(Serialize)] struct NewNick<'n> { id: u64, name: &'n str } let user = NewNick { id, name }; if let Err(e) = notify("update-nick", user).await { eprintln!("[API-RTC] Unable to connect to RTC server: {}", e); } }