140 lines
3.9 KiB
Rust
140 lines
3.9 KiB
Rust
/*
|
|
* 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<u8> = {
|
|
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<P>(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);
|
|
}
|
|
}
|