forked from jmug/stoatchat
194 lines
6.6 KiB
Rust
194 lines
6.6 KiB
Rust
extern crate ws;
|
|
|
|
use crate::database;
|
|
|
|
use hashbrown::HashMap;
|
|
use std::sync::RwLock;
|
|
use ulid::Ulid;
|
|
|
|
use bson::doc;
|
|
use serde_json::{from_str, json, Value};
|
|
|
|
use ws::{listen, CloseCode, Error, Handler, Handshake, Message, Result, Sender};
|
|
|
|
struct Cell {
|
|
id: String,
|
|
out: Sender,
|
|
}
|
|
|
|
use once_cell::sync::OnceCell;
|
|
static mut CLIENTS: OnceCell<RwLock<HashMap<String, Vec<Cell>>>> = OnceCell::new();
|
|
|
|
struct Server {
|
|
out: Sender,
|
|
id: Option<String>,
|
|
internal: String,
|
|
}
|
|
|
|
impl Handler for Server {
|
|
fn on_open(&mut self, _: Handshake) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
fn on_message(&mut self, msg: Message) -> Result<()> {
|
|
if let Message::Text(text) = msg {
|
|
let data: Value = from_str(&text).unwrap();
|
|
|
|
if let Value::String(packet_type) = &data["type"] {
|
|
match packet_type.as_str() {
|
|
"authenticate" => {
|
|
if self.id.is_some() {
|
|
self.out.send(
|
|
json!({
|
|
"type": "authenticate",
|
|
"success": false,
|
|
"error": "Already authenticated!"
|
|
})
|
|
.to_string(),
|
|
)
|
|
} else if let Value::String(token) = &data["token"] {
|
|
let col = database::get_collection("users");
|
|
|
|
match col.find_one(doc! { "access_token": token }, None).unwrap() {
|
|
Some(u) => {
|
|
let id = u.get_str("_id").expect("Missing id.");
|
|
|
|
unsafe {
|
|
let mut map = CLIENTS.get_mut().unwrap().write().unwrap();
|
|
let cell = Cell {
|
|
id: self.internal.clone(),
|
|
out: self.out.clone(),
|
|
};
|
|
if map.contains_key(&id.to_string()) {
|
|
map.get_mut(&id.to_string()).unwrap().push(cell);
|
|
} else {
|
|
map.insert(id.to_string(), vec![cell]);
|
|
}
|
|
}
|
|
|
|
println!(
|
|
"Websocket client connected. [ID: {} // {}]",
|
|
id.to_string(),
|
|
self.internal
|
|
);
|
|
|
|
self.id = Some(id.to_string());
|
|
self.out.send(
|
|
json!({
|
|
"type": "authenticate",
|
|
"success": true
|
|
})
|
|
.to_string(),
|
|
)
|
|
}
|
|
None => self.out.send(
|
|
json!({
|
|
"type": "authenticate",
|
|
"success": false,
|
|
"error": "Invalid authentication token."
|
|
})
|
|
.to_string(),
|
|
),
|
|
}
|
|
} else {
|
|
self.out.send(
|
|
json!({
|
|
"type": "authenticate",
|
|
"success": false,
|
|
"error": "Missing authentication token."
|
|
})
|
|
.to_string(),
|
|
)
|
|
}
|
|
}
|
|
_ => Ok(()),
|
|
}
|
|
} else {
|
|
Ok(())
|
|
}
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn on_close(&mut self, code: CloseCode, reason: &str) {
|
|
match code {
|
|
CloseCode::Normal => println!("The client is done with the connection."),
|
|
CloseCode::Away => println!("The client is leaving the site."),
|
|
CloseCode::Abnormal => {
|
|
println!("Closing handshake failed! Unable to obtain closing status from client.")
|
|
}
|
|
_ => println!("The client encountered an error: {}", reason),
|
|
}
|
|
|
|
if let Some(id) = &self.id {
|
|
println!(
|
|
"Websocket client disconnected. [ID: {} // {}]",
|
|
id, self.internal
|
|
);
|
|
unsafe {
|
|
let mut map = CLIENTS.get_mut().unwrap().write().unwrap();
|
|
let arr = map.get_mut(&id.clone()).unwrap();
|
|
|
|
if arr.len() == 1 {
|
|
map.remove(&id.clone());
|
|
} else {
|
|
let index = arr.iter().position(|x| x.id == self.internal).unwrap();
|
|
arr.remove(index);
|
|
println!(
|
|
"User [{}] is still connected {} times",
|
|
self.id.as_ref().unwrap(),
|
|
arr.len()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn on_error(&mut self, err: Error) {
|
|
println!("The server encountered an error: {:?}", err);
|
|
}
|
|
}
|
|
|
|
pub fn launch_server() {
|
|
unsafe {
|
|
if CLIENTS.set(RwLock::new(HashMap::new())).is_err() {
|
|
panic!("Failed to set CLIENTS map!");
|
|
}
|
|
}
|
|
|
|
listen("192.168.0.10:9999", |out| Server {
|
|
out: out,
|
|
id: None,
|
|
internal: Ulid::new().to_string(),
|
|
})
|
|
.unwrap()
|
|
}
|
|
|
|
pub fn send_message(id: String, message: String) -> std::result::Result<(), ()> {
|
|
unsafe {
|
|
let map = CLIENTS.get().unwrap().read().unwrap();
|
|
if map.contains_key(&id) {
|
|
let arr = map.get(&id).unwrap();
|
|
|
|
for item in arr {
|
|
if item.out.send(message.clone()).is_err() {
|
|
return Err(());
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
// ! TODO: WRITE THREADED QUEUE SYSTEM
|
|
// ! FETCH RECIPIENTS HERE INSTEAD OF IN METHOD
|
|
|
|
pub fn queue_message(ids: Vec<String>, message: String) {
|
|
for id in ids {
|
|
send_message(id, message.clone())
|
|
.expect("uhhhhhhhhhh can i get uhhhhhhhhhhhhhhhhhh mcdonald cheese burger with fries");
|
|
}
|
|
}
|