diff --git a/src/auth/auth_connector.rs b/src/auth/auth_connector.rs index 2dd91f8..c52a9fd 100644 --- a/src/auth/auth_connector.rs +++ b/src/auth/auth_connector.rs @@ -1,5 +1,7 @@ use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; use crate::util::config_util::CONFIG; +use crate::util::print::{PrintType, line}; +use json::number::Number; use reqwest::{Client, Response}; use std::time::Duration; use uuid::Uuid; @@ -64,7 +66,7 @@ pub async fn get_user(user_id: Uuid) -> Option { }) } -pub async fn get_iota_id(user_id: Uuid) -> Option { +pub async fn get_iota_id(user_id: i64) -> Option { let url = format!("https://auth.tensamin.net/api/get/iota-id/{}", user_id); let client = client(); @@ -81,13 +83,17 @@ pub async fn get_iota_id(user_id: Uuid) -> Option { let cv = CommunicationValue::from_json(&json); if cv.comm_type != CommunicationType::success { + line(PrintType::IotaIn, &cv.to_json().to_string()); return None; } - let iota_id_str = cv.get_data(DataTypes::iota_id)?.to_string(); - Uuid::parse_str(&iota_id_str).ok() + let iota_id = cv.get_data(DataTypes::iota_id)?.as_i64().unwrap_or(0); + if iota_id == 0 { + return None; + } + Some(iota_id) } -pub async fn is_private_key_valid(user_id: Uuid, pk_hash: &str) -> bool { +pub async fn is_private_key_valid(user_id: i64, pk_hash: &str) -> bool { let url = format!( "https://auth.tensamin.net/api/get/private-key-hash/{}/", user_id @@ -120,7 +126,7 @@ pub async fn is_private_key_valid(user_id: Uuid, pk_hash: &str) -> bool { None => false, } } -pub async fn get_public_key(user_id: Uuid) -> Option { +pub async fn get_public_key(user_id: i64) -> Option { let url = format!("https://auth.tensamin.net/api/{}/public-key", user_id); let client = client(); @@ -141,14 +147,16 @@ pub async fn get_public_key(user_id: Uuid) -> Option { Some(cv.get_data(DataTypes::ping_clients)?.to_string()) } -pub async fn get_register() -> Option { +pub async fn get_register() -> Option { let url = "https://auth.tensamin.net/api/register/init".to_string(); let client = client(); let res = client.get(&url).send().await.ok()?; let json = res.text().await.ok()?; let cv = CommunicationValue::from_json(&json); - Uuid::parse_str(&*cv.get_data(DataTypes::user_id).unwrap().to_string()).ok() + cv.get_data(DataTypes::user_id) + .unwrap_or(&json::JsonValue::Number(Number::from(0))) + .as_i64() } async fn handle_response(resp: Response) -> bool { diff --git a/src/calls/call_group.rs b/src/calls/call_group.rs index d597a50..286e6f5 100644 --- a/src/calls/call_group.rs +++ b/src/calls/call_group.rs @@ -8,6 +8,7 @@ use crate::calls::caller::Caller; pub struct CallGroup { pub call_id: Uuid, pub members: RwLock>>, + pub show: RwLock, } impl CallGroup { @@ -15,13 +16,15 @@ impl CallGroup { CallGroup { call_id, members: RwLock::new(vec![user]), + show: RwLock::new(true), } } - pub async fn add_member(self: Arc, member: Uuid, inviter: Uuid) { + pub async fn add_member(self: Arc, member: i64, inviter: i64) { + *self.show.write().await = true; self.members .write() .await - .push(Arc::new(Caller::new(member, inviter, self.call_id))); + .push(Arc::new(Caller::new(member, self.call_id, inviter))); } } diff --git a/src/calls/call_manager.rs b/src/calls/call_manager.rs index 0d30a79..ad6eb1e 100644 --- a/src/calls/call_manager.rs +++ b/src/calls/call_manager.rs @@ -1,13 +1,17 @@ +use livekit_api::services::room::RoomClient; use once_cell::sync::Lazy; -use std::sync::Arc; +use std::{str::FromStr, sync::Arc, time::Duration}; use tokio::sync::RwLock; use uuid::Uuid; -use crate::calls::{call_group::CallGroup, caller::Caller}; +use crate::{ + calls::{call_group::CallGroup, caller::Caller}, + util::print::{PrintType, line}, +}; static CALL_GROUPS: Lazy>>> = Lazy::new(|| RwLock::new(Vec::new())); -pub async fn get_call_invites(user_id: Uuid) -> Vec> { +pub async fn get_call_invites(user_id: i64) -> Vec> { let mut callers = Vec::new(); for cg in CALL_GROUPS.read().await.iter() { let members = cg.members.read().await; @@ -20,7 +24,7 @@ pub async fn get_call_invites(user_id: Uuid) -> Vec> { callers } -pub async fn get_call_groups(user_id: Uuid) -> Vec> { +pub async fn get_call_groups(user_id: i64) -> Vec> { let mut call_groups = Vec::new(); for cg in CALL_GROUPS.read().await.iter() { let is_member = { @@ -35,7 +39,7 @@ pub async fn get_call_groups(user_id: Uuid) -> Vec> { call_groups } -pub async fn get_call_token(user_id: Uuid, call_id: Uuid) -> Option { +pub async fn get_call_token(user_id: i64, call_id: Uuid) -> Option { let existing_group = { let call_groups = CALL_GROUPS.read().await; call_groups.iter().find(|g| g.call_id == call_id).cloned() @@ -80,11 +84,13 @@ pub async fn get_call_token(user_id: Uuid, call_id: Uuid) -> Option { Some(caller.create_token()) } -pub async fn add_invite(call_id: Uuid, inviter_id: Uuid, invitee_id: Uuid) -> bool { - let target_group = { - let call_groups = CALL_GROUPS.read().await; - call_groups.iter().find(|g| g.call_id == call_id).cloned() - }; +pub async fn add_invite(call_id: Uuid, inviter_id: i64, invitee_id: i64) -> bool { + let target_group: Option> = CALL_GROUPS + .read() + .await + .iter() + .find(|g| g.call_id == call_id) + .cloned(); if let Some(cg) = target_group { let mut members = cg.members.write().await; @@ -100,18 +106,45 @@ pub async fn add_invite(call_id: Uuid, inviter_id: Uuid, invitee_id: Uuid) -> bo } false } - -pub async fn get_call_group_by_user(user_id: Uuid) -> Option> { - let call_groups = CALL_GROUPS.read().await; - for cg in call_groups.iter() { - let is_member = { - let members = cg.members.read().await; - members.iter().any(|m| m.user_id == user_id) - }; - - if is_member { - return Some(cg.clone()); +pub fn garbage_collect_calls() { + tokio::spawn(async move { + loop { + clean_calls().await; + tokio::time::sleep(Duration::from_secs(2)).await; + } + }); +} +pub async fn clean_calls() { + let room_service = RoomClient::new("https://call.tensamin.net").unwrap(); + let rooms = room_service.list_rooms(Vec::new()).await.unwrap(); + let mut call_ids: Vec = Vec::new(); + let mut no_users: Vec = Vec::new(); + for room in rooms { + if let Ok(id) = Uuid::from_str(&room.name) { + if room.num_participants == 0 { + no_users.push(id); + } + call_ids.push(id); } } - None + let mut call_groups = CALL_GROUPS.write().await; + let size_pre = call_groups.len(); + call_groups.retain(|cg| call_ids.contains(&cg.call_id)); + + for cg in call_groups.iter() { + *cg.show.write().await = !no_users.contains(&cg.call_id); + } + + let size_post = call_groups.len(); + drop(call_groups); + if size_pre - size_post != 0 { + line( + PrintType::CallIn, + &format!( + "Cleaned {} calls, {} remaining", + size_pre - size_post, + size_post + ), + ); + } } diff --git a/src/calls/call_util.rs b/src/calls/call_util.rs index f913216..8480555 100644 --- a/src/calls/call_util.rs +++ b/src/calls/call_util.rs @@ -2,10 +2,7 @@ use livekit_api::access_token; use std::env; use uuid::Uuid; -pub fn create_token( - user_id: Uuid, - call_id: Uuid, -) -> Result { +pub fn create_token(user_id: i64, call_id: Uuid) -> Result { let api_key = env::var("LIVEKIT_API_KEY").expect("LIVEKIT_API_KEY is not set"); let api_secret = env::var("LIVEKIT_API_SECRET").expect("LIVEKIT_API_SECRET is not set"); diff --git a/src/calls/caller.rs b/src/calls/caller.rs index da41876..b804ada 100644 --- a/src/calls/caller.rs +++ b/src/calls/caller.rs @@ -3,13 +3,13 @@ use uuid::Uuid; use crate::calls::call_util; pub struct Caller { - pub user_id: Uuid, + pub user_id: i64, pub call_id: Uuid, - pub inviters: Vec, + pub inviters: Vec, } impl Caller { - pub fn new(user_id: Uuid, call_id: Uuid, inviter_id: Uuid) -> Self { + pub fn new(user_id: i64, call_id: Uuid, inviter_id: i64) -> Self { Caller { user_id, call_id, diff --git a/src/data/communication.rs b/src/data/communication.rs index 6a62f5f..0620c36 100644 --- a/src/data/communication.rs +++ b/src/data/communication.rs @@ -1,7 +1,6 @@ use json::number::Number; use json::{Array, JsonValue, object, parse}; use std::collections::HashMap; -use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; use uuid::Uuid; @@ -20,6 +19,7 @@ pub enum DataTypes { user_state, user_states, user_pings, + call_state, screen_share, private_key_hash, accepted, @@ -73,11 +73,12 @@ pub enum DataTypes { challenge, community_title, communities, + + user, } impl DataTypes { pub fn parse(p0: String) -> DataTypes { - // normalize: lowercase + remove underscores let normalized = p0.to_lowercase().replace('_', ""); match normalized.as_str() { @@ -92,6 +93,7 @@ impl DataTypes { "userstate" => DataTypes::user_state, "userstates" => DataTypes::user_states, "userpings" => DataTypes::user_pings, + "callstate" => DataTypes::call_state, "screenshare" => DataTypes::screen_share, "privatekeyhash" => DataTypes::private_key_hash, "accepted" => DataTypes::accepted, @@ -145,6 +147,8 @@ impl DataTypes { "challenge" => DataTypes::challenge, "communitytitle" => DataTypes::community_title, "communities" => DataTypes::communities, + + "user" => DataTypes::user, _ => DataTypes::error_type, // fallback if unknown } } @@ -160,6 +164,9 @@ pub enum CommunicationType { error_invalid_challenge, error_invalid_secret, error_invalid_private_key, + error_no_user_id, + error_no_call_id, + error_invalid_call_id, success, settings_save, settings_load, @@ -178,11 +185,11 @@ pub enum CommunicationType { add_community, remove_community, get_communities, + challenge, + challenge_response, register, register_response, identification, - challenge, - challenge_response, identification_response, ping, pong, @@ -203,25 +210,42 @@ pub enum CommunicationType { watch_stream, call_token, call_invite, + end_call, function, update, + create_user, } impl CommunicationType { pub fn parse(p0: String) -> CommunicationType { let normalized = p0.to_lowercase().replace('_', ""); match normalized.as_str() { - "error" => CommunicationType::error, + "watchstream" => CommunicationType::watch_stream, + "calltoken" => CommunicationType::call_token, + "callinvite" => CommunicationType::call_invite, + "endcall" => CommunicationType::end_call, + "function" => CommunicationType::function, + "update" => CommunicationType::update, + "createuser" => CommunicationType::create_user, + "errorinvaliduserid" => CommunicationType::error_invalid_user_id, + "errornotfound" => CommunicationType::error_not_found, + "errornoiota" => CommunicationType::error_no_iota, + "errorinvalidchallenge" => CommunicationType::error_invalid_challenge, + "errorinvalidsecret" => CommunicationType::error_invalid_secret, + "errorinvalidprivatekey" => CommunicationType::error_invalid_private_key, + "errornouserid" => CommunicationType::error_no_user_id, + "errornocallid" => CommunicationType::error_no_call_id, + "errorinvalidcallid" => CommunicationType::error_invalid_call_id, + "success" => CommunicationType::success, "settingssave" => CommunicationType::settings_save, "settingsload" => CommunicationType::settings_load, "settingslist" => CommunicationType::settings_list, - "success" => CommunicationType::success, "message" => CommunicationType::message, + "messagesend" => CommunicationType::message_send, "messagelive" => CommunicationType::message_live, - "messageotheriota" => CommunicationType::message_other_iota, + "messageother_iota" => CommunicationType::message_other_iota, "messagechunk" => CommunicationType::message_chunk, "messagesget" => CommunicationType::messages_get, - "messagesend" => CommunicationType::message_send, "changeconfirm" => CommunicationType::change_confirm, "confirmreceive" => CommunicationType::confirm_receive, "confirmread" => CommunicationType::confirm_read, @@ -230,11 +254,11 @@ impl CommunicationType { "addcommunity" => CommunicationType::add_community, "removecommunity" => CommunicationType::remove_community, "getcommunities" => CommunicationType::get_communities, + "challenge" => CommunicationType::challenge, + "challengeresponse" => CommunicationType::challenge_response, "register" => CommunicationType::register, "registerresponse" => CommunicationType::register_response, "identification" => CommunicationType::identification, - "challenge" => CommunicationType::challenge, - "challengeresponse" => CommunicationType::challenge_response, "identificationresponse" => CommunicationType::identification_response, "ping" => CommunicationType::ping, "pong" => CommunicationType::pong, @@ -252,11 +276,7 @@ impl CommunicationType { "webrtcice" => CommunicationType::webrtc_ice, "startstream" => CommunicationType::start_stream, "endstream" => CommunicationType::end_stream, - "watchstream" => CommunicationType::watch_stream, - "calltoken" => CommunicationType::call_token, - "callinvite" => CommunicationType::call_invite, - "function" => CommunicationType::function, - "update" => CommunicationType::update, + _ => CommunicationType::error, } } @@ -266,8 +286,8 @@ impl CommunicationType { pub struct CommunicationValue { pub id: Uuid, pub comm_type: CommunicationType, - pub sender: Uuid, - pub receiver: Uuid, + pub sender: i64, + pub receiver: i64, pub data: HashMap, } @@ -277,8 +297,8 @@ impl CommunicationValue { Self { id: Uuid::new_v4(), comm_type, - sender: Uuid::new_v4(), - receiver: Uuid::new_v4(), + sender: 0, + receiver: 0, data: HashMap::new(), } } @@ -289,18 +309,18 @@ impl CommunicationValue { pub fn get_id(&self) -> Uuid { self.id.clone() } - pub fn with_sender(mut self, sender: Uuid) -> Self { + pub fn with_sender(mut self, sender: i64) -> Self { self.sender = sender; self } - pub fn get_sender(&self) -> Uuid { + pub fn get_sender(&self) -> i64 { self.sender.clone() } - pub fn with_receiver(mut self, receiver: Uuid) -> Self { + pub fn with_receiver(mut self, receiver: i64) -> Self { self.receiver = receiver; self } - pub fn get_receiver(&self) -> Uuid { + pub fn get_receiver(&self) -> i64 { self.receiver.clone() } pub fn add_data_num(mut self, key: DataTypes, value: Number) -> Self { @@ -331,72 +351,104 @@ impl CommunicationValue { for (k, v) in &self.data { jdata[&format!("{:?}", k)] = JsonValue::from(v.clone()); } - - object! { - id: self.id.to_string(), - type: format!("{:?}", self.comm_type), - sender: self.sender.to_string(), - receiver: self.receiver.to_string(), - data: jdata + if self.sender > 0 && self.receiver > 0 { + object! { + id: self.id.to_string(), + type: format!("{:?}", self.comm_type), + sender: self.sender.to_string(), + receiver: self.receiver.to_string(), + data: jdata + } + } else if self.sender > 0 { + object! { + id: self.id.to_string(), + type: format!("{:?}", self.comm_type), + sender: self.sender.to_string(), + data: jdata + } + } else if self.receiver > 0 { + object! { + id: self.id.to_string(), + type: format!("{:?}", self.comm_type), + receiver: self.receiver.to_string(), + data: jdata + } + } else { + object! { + id: self.id.to_string(), + type: format!("{:?}", self.comm_type), + data: jdata + } } } pub fn from_json(json_str: &str) -> Self { - let parsed = parse(json_str).unwrap(); - - let comm_type = CommunicationType::parse(parsed["type"].to_string()); - - let sender: Uuid = parsed["sender"] - .as_str() - .and_then(|s| Uuid::parse_str(s).ok()) - .unwrap_or(Uuid::new_v4()); - let receiver: Uuid = parsed["receiver"] - .as_str() - .and_then(|s| Uuid::parse_str(s).ok()) - .unwrap_or(Uuid::new_v4()); - - let uuid = Uuid::parse_str(parsed["id"].as_str().unwrap_or("")).unwrap_or(Uuid::new_v4()); - let mut data = HashMap::new(); - if parsed["data"].is_object() { - for (k, v) in parsed["data"].entries() { - data.insert(DataTypes::parse(k.to_string()), v.clone()); + if let Ok(parsed) = parse(json_str) { + let comm_type = CommunicationType::parse(parsed["type"].to_string()); + let mut sender: i64 = 0; + if parsed.has_key("sender") { + sender = parsed["sender"].as_i64().unwrap_or(0); + } + let mut receiver: i64 = 0; + if parsed.has_key("receiver") { + receiver = parsed["receiver"].as_i64().unwrap_or(0); } - } - Self { - id: uuid, - comm_type, - sender, - receiver, - data, + let uuid = + Uuid::parse_str(parsed["id"].as_str().unwrap_or("")).unwrap_or(Uuid::new_v4()); + let mut data = HashMap::new(); + if parsed["data"].is_object() { + for (k, v) in parsed["data"].entries() { + data.insert(DataTypes::parse(k.to_string()), v.clone()); + } + } + + Self { + id: uuid, + comm_type, + sender, + receiver, + data, + } + } else { + Self { + id: Uuid::new_v4(), + comm_type: CommunicationType::error, + sender: 0, + receiver: 0, + data: HashMap::new(), + } } } pub fn forward_to_other_iota(original: &mut CommunicationValue) -> CommunicationValue { - let receiver = Uuid::from_str( - &*original - .get_data(DataTypes::receiver_id) - .unwrap() - .to_string(), - ) - .ok() - .or(Option::from(Uuid::nil())); + let receiver = original + .get_data(DataTypes::receiver_id) + .unwrap_or(&JsonValue::Number(Number::from(0))) + .as_i64() + .unwrap_or(0); let now_ms = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_millis() as i64; - let cv = CommunicationValue::new(CommunicationType::message_other_iota) + let sender = original.get_sender(); + CommunicationValue::new(CommunicationType::message_other_iota) .with_id(original.get_id()) - .with_receiver(receiver.unwrap()) + .with_receiver(receiver) + .add_data( + DataTypes::receiver_id, + JsonValue::Number(Number::from(receiver)), + ) + .with_sender(sender) .add_data(DataTypes::send_time, JsonValue::String(now_ms.to_string())) + .add_data( + DataTypes::sender_id, + JsonValue::Number(Number::from(sender)), + ) .add_data( DataTypes::content, JsonValue::String(original.get_data(DataTypes::content).unwrap().to_string()), - ); - - // include sender_id if the original had one - let sender = original.get_sender(); - cv.add_data(DataTypes::sender_id, JsonValue::String(sender.to_string())) + ) } } diff --git a/src/data/user.rs b/src/data/user.rs index 3516787..306699e 100644 --- a/src/data/user.rs +++ b/src/data/user.rs @@ -1,13 +1,11 @@ -use uuid::Uuid; - #[derive(Clone)] pub struct User { - pub iota_id: Uuid, - pub user_id: Uuid, + pub iota_id: i64, + pub user_id: i64, pub status: UserStatus, } impl User { - pub fn new(iota_id: Uuid, user_id: Uuid, status: UserStatus) -> Self { + pub fn new(iota_id: i64, user_id: i64, status: UserStatus) -> Self { User { iota_id, user_id, diff --git a/src/main.rs b/src/main.rs index 4fa79da..491cb3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ use tokio_util::compat::TokioAsyncReadCompatExt; use tungstenite::handshake::server::{Request, Response}; use crate::{ + calls::call_manager::garbage_collect_calls, omega::omega_connection::OmegaConnection, rho::{client_connection::ClientConnection, iota_connection::IotaConnection}, util::{ @@ -36,6 +37,8 @@ async fn main() { &format!("WebSocket server listening on {}", &address), ); + garbage_collect_calls(); + while let Ok((stream, _)) = listener.accept().await { tokio::spawn(async move { let mut path: String = "/".to_string(); diff --git a/src/omega/omega_connection.rs b/src/omega/omega_connection.rs index b7037c5..107f75f 100644 --- a/src/omega/omega_connection.rs +++ b/src/omega/omega_connection.rs @@ -104,14 +104,16 @@ impl OmegaConnection { // Handle CLIENT_CHANGED if cv.is_type(CommunicationType::client_changed) { - let iota_id = Uuid::parse_str( - cv.get_data(DataTypes::iota_id).unwrap().as_str().unwrap(), - ) - .unwrap(); - let user_id = Uuid::parse_str( - cv.get_data(DataTypes::user_id).unwrap().as_str().unwrap(), - ) - .unwrap(); + let iota_id = cv + .get_data(DataTypes::iota_id) + .unwrap() + .as_i64() + .unwrap_or(0); + let user_id = cv + .get_data(DataTypes::user_id) + .unwrap() + .as_i64() + .unwrap_or(0); let status_str = cv .get_data(DataTypes::user_state) .unwrap() @@ -147,7 +149,7 @@ impl OmegaConnection { } } - pub async fn connect_iota(iota_id: Uuid, user_ids: Vec) { + pub async fn connect_iota(iota_id: i64, user_ids: Vec) { let user_ids_str = user_ids .iter() .map(|id| id.to_string()) @@ -159,21 +161,21 @@ impl OmegaConnection { OmegaConnection::send_global(cv).await; } - pub async fn close_iota(iota_id: Uuid) { + pub async fn close_iota(iota_id: i64) { let cv = CommunicationValue::new(CommunicationType::iota_closed) .add_data(DataTypes::iota_id, JsonValue::from(iota_id.to_string())); OmegaConnection::send_global(cv).await; } - pub async fn client_changed(iota_id: Uuid, user_id: Uuid, state: UserStatus) { + pub async fn client_changed(iota_id: i64, user_id: i64, state: UserStatus) { let cv = CommunicationValue::new(CommunicationType::client_changed) - .add_data(DataTypes::iota_id, JsonValue::from(iota_id.to_string())) - .add_data(DataTypes::user_id, JsonValue::from(user_id.to_string())) + .add_data(DataTypes::iota_id, JsonValue::from(iota_id)) + .add_data(DataTypes::user_id, JsonValue::from(user_id)) .add_data(DataTypes::user_state, JsonValue::from(state.to_string())); OmegaConnection::send_global(cv).await; } - pub async fn user_states(user_id: Uuid, user_ids: Vec) { + pub async fn user_states(user_id: i64, user_ids: Vec) { let user_ids_str = user_ids .iter() .map(|id| id.to_string()) diff --git a/src/rho/client_connection.rs b/src/rho/client_connection.rs index 479ba0f..8f97671 100644 --- a/src/rho/client_connection.rs +++ b/src/rho/client_connection.rs @@ -1,5 +1,6 @@ use async_tungstenite::tungstenite::Message; use async_tungstenite::{WebSocketReceiver, WebSocketSender}; +use json::number::Number; use std::sync::{Arc, Weak}; use tokio::sync::RwLock; use tokio_util::compat::Compat; @@ -27,7 +28,7 @@ pub struct ClientConnection { pub sender: Arc>>>, pub receiver: Arc>>>, /// User ID associated with this client - pub user_id: Arc>>, + pub user_id: Arc>, /// Whether this connection has been identified/authenticated pub identified: Arc>, /// Ping latency tracking @@ -35,7 +36,7 @@ pub struct ClientConnection { /// Weak reference to RhoConnection to avoid circular references pub rho_connection: Arc>>>, /// List of user IDs this client is interested in receiving updates about - pub interested_users: Arc>>, + pub interested_users: Arc>>, } impl ClientConnection { @@ -47,7 +48,7 @@ impl ClientConnection { Arc::new(Self { sender: Arc::new(RwLock::new(sender)), receiver: Arc::new(RwLock::new(receiver)), - user_id: Arc::new(RwLock::new(None)), + user_id: Arc::new(RwLock::new(0)), identified: Arc::new(RwLock::new(false)), ping: Arc::new(RwLock::new(-1)), rho_connection: Arc::new(RwLock::new(None)), @@ -56,7 +57,7 @@ impl ClientConnection { } /// Get the user ID - pub async fn get_user_id(&self) -> Option { + pub async fn get_user_id(&self) -> i64 { *self.user_id.read().await } @@ -155,18 +156,8 @@ impl ClientConnection { /// Handle identification message async fn handle_identification(&self, sarc: Arc, cv: CommunicationValue) { // Extract user ID - let user_id = match cv.get_data(DataTypes::user_id) { - Some(id_str) => match Uuid::parse_str(&id_str.to_string()) { - Ok(id) => id, - Err(_) => { - self.send_error_response( - &cv.get_id(), - CommunicationType::error_invalid_user_id, - ) - .await; - return; - } - }, + let user_id: i64 = match cv.get_data(DataTypes::user_id) { + Some(id_str) => id_str.as_i64().unwrap_or(0), None => { self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_user_id) .await; @@ -209,7 +200,7 @@ impl ClientConnection { // Set identification data { let mut user_id_guard = self.user_id.write().await; - *user_id_guard = Some(user_id); + *user_id_guard = user_id; } { let mut identified_guard = self.identified.write().await; @@ -253,61 +244,53 @@ impl ClientConnection { /// Handle client status change async fn handle_client_changed(&self, cv: CommunicationValue) { - if let Some(user_id) = self.get_user_id().await { - if let Some(_status_str) = cv.get_data(DataTypes::user_state) { - // Parse user status - this would need to be implemented properly - let user_status = UserStatus::online; // placeholder - if let Some(rho_conn) = self.get_rho_connection().await { - OmegaConnection::client_changed( - rho_conn.get_iota_id().await, - user_id, - user_status, - ) + let user_id = self.get_user_id().await; + if let Some(_status_str) = cv.get_data(DataTypes::user_state) { + // Parse user status - this would need to be implemented properly + let user_status = UserStatus::online; // placeholder + if let Some(rho_conn) = self.get_rho_connection().await { + OmegaConnection::client_changed(rho_conn.get_iota_id().await, user_id, user_status) .await; - } } } } /// Handle call invite async fn handle_call_invite(&self, cv: CommunicationValue) { - let receiver_id = match cv.get_data(DataTypes::receiver_id) { - Some(id_str) => match Uuid::parse_str(&id_str.to_string()) { - Ok(id) => id, - Err(_) => { - self.send_error_response(&cv.get_id(), CommunicationType::error) - .await; - return; - } - }, - None => { - self.send_error_response(&cv.get_id(), CommunicationType::error) - .await; - return; - } - }; + let receiver_id: i64 = cv + .get_data(DataTypes::receiver_id) + .unwrap_or(&json::JsonValue::Number(Number::from(0))) + .as_i64() + .unwrap_or(0); + if receiver_id == 0 { + self.send_error_response(&cv.get_id(), CommunicationType::error_no_user_id) + .await; + return; + } let call_id = match cv.get_data(DataTypes::call_id) { Some(id_str) => match Uuid::parse_str(&id_str.to_string()) { Ok(id) => id, Err(_) => { - self.send_error_response(&cv.get_id(), CommunicationType::error) - .await; + self.send_error_response( + &cv.get_id(), + CommunicationType::error_invalid_call_id, + ) + .await; return; } }, None => { - self.send_error_response(&cv.get_id(), CommunicationType::error) + self.send_error_response(&cv.get_id(), CommunicationType::error_no_call_id) .await; return; } }; let invited = - call_manager::add_invite(call_id, self.user_id.read().await.unwrap(), receiver_id) - .await; - if invited { - self.send_error_response(&cv.get_id(), CommunicationType::error) + call_manager::add_invite(call_id, *self.user_id.read().await, receiver_id).await; + if !invited { + self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_call_id) .await; return; } @@ -323,10 +306,7 @@ impl ClientConnection { }; // Get sender user ID - let sender_id = match self.get_user_id().await { - Some(id) => id, - None => return, - }; + let sender_id = self.get_user_id().await; // Create and send call distribution message let forward = CommunicationValue::new(CommunicationType::call_invite) @@ -344,10 +324,7 @@ impl ClientConnection { /// Handle get call request async fn handle_get_call(&self, cv: CommunicationValue) { - let user_id = match self.get_user_id().await { - Some(id) => id, - None => return, - }; + let user_id = self.get_user_id().await; let call_id = match cv.get_data(DataTypes::call_id) { Some(id_str) => match Uuid::parse_str(&id_str.to_string()) { @@ -380,11 +357,10 @@ impl ClientConnection { /// Forward message to Iota async fn forward_to_iota(&self, cv: CommunicationValue) { - if let Some(user_id) = self.get_user_id().await { - if let Some(rho_conn) = self.get_rho_connection().await { - let updated_cv = cv.with_sender(user_id); - rho_conn.message_to_iota(updated_cv).await; - } + let user_id = self.get_user_id().await; + if let Some(rho_conn) = self.get_rho_connection().await { + let updated_cv = cv.with_sender(user_id); + rho_conn.message_to_iota(updated_cv).await; } } @@ -401,7 +377,7 @@ impl ClientConnection { } /// Set interested users list - pub async fn set_interested_users(&self, interested_ids: Vec) { + pub async fn set_interested_users(&self, interested_ids: Vec) { let mut interested_guard = self.interested_users.write().await; *interested_guard = interested_ids; } @@ -424,12 +400,11 @@ impl ClientConnection { /// Handle connection close pub async fn handle_close(&self) { if self.is_identified().await { - if let Some(user_id) = self.get_user_id().await { - if let Some(rho_conn) = rho_manager::get_rho_con_for_user(user_id).await { - rho_conn - .close_client_connection(Arc::new(self.clone())) - .await; - } + let user_id = self.get_user_id().await; + if let Some(rho_conn) = rho_manager::get_rho_con_for_user(user_id).await { + rho_conn + .close_client_connection(Arc::new(self.clone())) + .await; } } } diff --git a/src/rho/iota_connection.rs b/src/rho/iota_connection.rs index a2378cf..1293328 100644 --- a/src/rho/iota_connection.rs +++ b/src/rho/iota_connection.rs @@ -7,6 +7,7 @@ use async_tungstenite::WebSocketReceiver; use async_tungstenite::WebSocketSender; use async_tungstenite::tungstenite::Message; use json::JsonValue; +use json::number::Number; use std::{ collections::HashMap, sync::{Arc, Weak}, @@ -27,8 +28,8 @@ use crate::{ pub struct IotaConnection { pub sender: Arc>>>, pub receiver: Arc>>>, - pub iota_id: Arc>, - pub user_ids: Arc>>, + pub iota_id: Arc>, + pub user_ids: Arc>>, pub identified: Arc>, pub ping: Arc>, pub rho_connection: Arc>>>, @@ -43,7 +44,7 @@ impl IotaConnection { Arc::new(Self { sender: Arc::new(RwLock::new(sender)), receiver: Arc::new(RwLock::new(receiver)), - iota_id: Arc::new(RwLock::new(Uuid::nil())), + iota_id: Arc::new(RwLock::new(0)), user_ids: Arc::new(RwLock::new(Vec::new())), identified: Arc::new(RwLock::new(false)), ping: Arc::new(RwLock::new(0)), @@ -52,12 +53,12 @@ impl IotaConnection { } /// Get the Iota ID - pub async fn get_iota_id(&self) -> Uuid { + pub async fn get_iota_id(&self) -> i64 { *self.iota_id.read().await } /// Get the user IDs - pub async fn get_user_ids(&self) -> Vec { + pub async fn get_user_ids(&self) -> Vec { self.user_ids.read().await.clone() } @@ -115,6 +116,7 @@ impl IotaConnection { // Handle identification if cv.is_type(CommunicationType::identification) && !self.is_identified().await { + line(PrintType::IotaIn, &cv.to_json().to_string()); self.handle_identification(cv).await; return; } @@ -151,35 +153,47 @@ impl IotaConnection { /// Handle identification message async fn handle_identification(self: Arc, cv: CommunicationValue) { - // Parse Iota ID - let iota_id = match cv.get_data(DataTypes::iota_id) { - Some(id_str) => match Uuid::parse_str(&id_str.to_string()) { - Ok(id) => id, - Err(_) => { - self.send_error_response(&cv.get_id()).await; - return; - } - }, - None => { - self.send_error_response(&cv.get_id()).await; - return; - } - }; + let iota_id: i64 = cv + .get_data(DataTypes::iota_id) + .unwrap_or(&JsonValue::Number(Number::from(0))) + .as_i64() + .unwrap_or(0); + + if iota_id == 0 { + let error = CommunicationValue::new(CommunicationType::error).with_id(cv.get_id()); + self.send_message(error).await; + return; + } // Parse user IDs - let mut validated_user_ids: Vec = Vec::new(); + let mut validated_user_ids: Vec = Vec::new(); if let Some(user_ids_str) = cv.get_data(DataTypes::user_ids) { for id_str in user_ids_str.to_string().split(',') { - if id_str.is_empty() { - continue; - } - if let Ok(user_id) = Uuid::parse_str(id_str.trim()) { - if let Some(auth_iota_id) = auth_connector::get_iota_id(user_id).await { - if auth_iota_id == iota_id { - validated_user_ids.push(user_id); + match id_str.parse::() { + Ok(user_id) => { + if let Some(auth_iota_id) = auth_connector::get_iota_id(user_id).await { + line( + PrintType::IotaIn, + &format!( + "auth for {} should be {} is {}", + user_id, iota_id, auth_iota_id + ), + ); + if auth_iota_id == iota_id { + validated_user_ids.push(user_id); + } + } else { + line( + PrintType::IotaIn, + &format!("User ID {} not parsed", id_str.trim()), + ); } - } else { - line(PrintType::IotaIn, "User ID not found"); + } + Err(e) => { + line( + PrintType::IotaIn, + &format!("Failed to parse '{}' as i64: {:?}", id_str, e), + ); } } } @@ -280,10 +294,10 @@ impl IotaConnection { /// Handle GET_CHATS message async fn handle_get_chats(&self, cv: CommunicationValue) { let receiver_id = cv.get_receiver(); - let mut interested_ids: Vec = Vec::new(); + let mut interested_ids: Vec = Vec::new(); let calls: Vec> = call_manager::get_call_groups(receiver_id).await; - let mut invites: HashMap> = HashMap::new(); + let mut invites: HashMap> = HashMap::new(); let empty = &calls.is_empty(); for call in calls { for inviter in call.members.read().await.iter() { @@ -309,22 +323,19 @@ impl IotaConnection { JsonValue::new_array() } } else { - line(PrintType::CallIn, &format!("not empty: {:?}", invites)); let mut enrc_contacts = JsonValue::new_array(); if let Some(contacts_data) = cv.get_data(DataTypes::user_ids) { if let JsonValue::Array(user_ids) = contacts_data { for user_json in user_ids { - let user_id_str = user_json["user_id"].as_str().unwrap_or(""); - if let Ok(user_id) = Uuid::parse_str(&user_id_str) { - interested_ids.push(user_id); - let mut enriched_contact = JsonValue::new_object(); - let _ = enriched_contact.insert("user_id", user_id.to_string()); - let _ = enriched_contact.insert( - "calls", - JsonValue::Array(invites.get(&user_id).unwrap_or(&vec![]).clone()), - ); - let _ = enrc_contacts.push(enriched_contact); - } + let user_id = user_json["user_id"].as_i64().unwrap_or(0); + interested_ids.push(user_id); + let mut enriched_contact = JsonValue::new_object(); + let _ = enriched_contact.insert("user_id", user_id.to_string()); + let _ = enriched_contact.insert( + "calls", + JsonValue::Array(invites.get(&user_id).unwrap_or(&vec![]).clone()), + ); + let _ = enrc_contacts.push(enriched_contact); } } else { enrc_contacts = contacts_data.clone(); @@ -355,11 +366,6 @@ impl IotaConnection { } } - async fn send_error_response(&self, message_id: &Uuid) { - let error = CommunicationValue::new(CommunicationType::error).with_id(*message_id); - self.send_message(error).await; - } - pub async fn handle_close(&self) { if self.is_identified().await { if let Some(rho_conn) = self.get_rho_connection().await { diff --git a/src/rho/rho_connection.rs b/src/rho/rho_connection.rs index e0342b4..6905c0c 100644 --- a/src/rho/rho_connection.rs +++ b/src/rho/rho_connection.rs @@ -4,20 +4,20 @@ use crate::data::{ user::UserStatus, }; use crate::omega::omega_connection::OmegaConnection; +use json::{JsonValue, number::Number}; use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; -use uuid::Uuid; pub struct RhoConnection { iota_connection: Arc, - user_ids: Vec, + user_ids: Vec, client_connections: Arc>>>, } impl RhoConnection { /// Create a new RhoConnection - pub async fn new(iota_connection: Arc, user_ids: Vec) -> Self { + pub async fn new(iota_connection: Arc, user_ids: Vec) -> Self { let rho_connection = Self { iota_connection, user_ids: user_ids.clone(), @@ -30,11 +30,11 @@ impl RhoConnection { rho_connection } - pub async fn get_iota_id(&self) -> Uuid { + pub async fn get_iota_id(&self) -> i64 { self.iota_connection.get_iota_id().await } - pub fn get_user_ids(&self) -> &Vec { + pub fn get_user_ids(&self) -> &Vec { &self.user_ids } @@ -50,12 +50,12 @@ impl RhoConnection { /// Get client connections for a specific user pub async fn get_client_connections_for_user( &self, - user_id: Uuid, + user_id: i64, ) -> Vec> { let connections = self.client_connections.read().await; let mut collections = Vec::new(); for con in connections.iter() { - if con.get_user_id().await.unwrap() == user_id { + if con.get_user_id().await == user_id { collections.push(con.clone()); } } @@ -64,15 +64,10 @@ impl RhoConnection { /// Add a client connection pub async fn add_client_connection(&self, connection: Arc) { - let notification = CommunicationValue::new(CommunicationType::client_connected) - .add_data_str( - DataTypes::user_id, - connection - .get_user_id() - .await - .unwrap_or(Uuid::nil()) - .to_string(), - ); + let notification = CommunicationValue::new(CommunicationType::client_connected).add_data( + DataTypes::user_id, + JsonValue::Number(Number::from(connection.get_user_id().await)), + ); self.iota_connection.send_message(notification).await; @@ -83,7 +78,7 @@ impl RhoConnection { OmegaConnection::client_changed( self.get_iota_id().await, - connection.get_user_id().await.unwrap_or(Uuid::nil()), + connection.get_user_id().await, UserStatus::online, ) .await; @@ -94,12 +89,10 @@ impl RhoConnection { { let mut connections = self.client_connections.write().await; - let target_user_id = connection.get_user_id().await.unwrap(); + let target_user_id = connection.get_user_id().await; connections.retain(|con| { - futures::executor::block_on(async { - con.get_user_id().await.unwrap() != target_user_id - }) + futures::executor::block_on(async { con.get_user_id().await != target_user_id }) }); connections.push(Arc::clone(&connection)); @@ -108,7 +101,7 @@ impl RhoConnection { // Notify OmegaConnection OmegaConnection::client_changed( self.get_iota_id().await, - connection.get_user_id().await.unwrap_or(Uuid::nil()), + connection.get_user_id().await, UserStatus::user_offline, ) .await; @@ -131,15 +124,9 @@ impl RhoConnection { /// Send message from Iota to specific client pub async fn message_to_client(&self, cv: CommunicationValue) { - if let Some(receiver_id) = Some(cv.get_receiver()) { - let connections = self.client_connections.read().await; - for connection in connections.iter() { - if let Some(conn_user_id) = connection.get_user_id().await { - if conn_user_id == receiver_id { - connection.send_message(&cv).await; - } - } - } + let connections = self.client_connections.read().await; + for connection in connections.iter() { + connection.send_message(&cv).await; } } @@ -154,16 +141,15 @@ impl RhoConnection { } /// Set interested users for a specific client - pub async fn set_interested(&self, user_id: Uuid, interested_ids: Vec) { + pub async fn set_interested(&self, user_id: i64, interested_ids: Vec) { let connections = self.client_connections.read().await; for connection in connections.iter() { - if let Some(conn_user_id) = connection.get_user_id().await { - if conn_user_id == user_id { - connection - .set_interested_users(interested_ids.clone()) - .await; - break; - } + let conn_user_id = connection.get_user_id().await; + if conn_user_id == user_id { + connection + .set_interested_users(interested_ids.clone()) + .await; + break; } } } @@ -182,16 +168,15 @@ impl RhoConnection { let mut pings = HashMap::new(); for connection in connections.iter() { - if let Some(user_id) = connection.get_user_id().await { - pings.insert(user_id.to_string(), connection.get_ping().await); - } + let user_id = connection.get_user_id().await; + pings.insert(user_id.to_string(), connection.get_ping().await); } pings } /// Check if this RhoConnection contains a specific user ID - pub fn contains_user(&self, user_id: &Uuid) -> bool { + pub fn contains_user(&self, user_id: &i64) -> bool { self.user_ids.contains(user_id) } diff --git a/src/rho/rho_manager.rs b/src/rho/rho_manager.rs index 9478943..01afb2c 100644 --- a/src/rho/rho_manager.rs +++ b/src/rho/rho_manager.rs @@ -6,12 +6,11 @@ use std::{ sync::{Arc, LazyLock}, }; use tokio::sync::RwLock; -use uuid::Uuid; -pub static RHO_CONNECTIONS: LazyLock>>>> = +pub static RHO_CONNECTIONS: LazyLock>>>> = LazyLock::new(|| Arc::new(RwLock::new(HashMap::new()))); -pub async fn get_rho_con_for_user(user_id: Uuid) -> Option> { +pub async fn get_rho_con_for_user(user_id: i64) -> Option> { let connections = RHO_CONNECTIONS.read().await; line( PrintType::ClientIn, @@ -32,13 +31,13 @@ pub async fn get_rho_con_for_user(user_id: Uuid) -> Option> { None } -pub async fn contains_iota(iota_id: Uuid) -> bool { +pub async fn contains_iota(iota_id: i64) -> bool { let connections = RHO_CONNECTIONS.read().await; connections.contains_key(&iota_id) } /// Remove a RhoConnection by Iota ID -pub async fn remove_rho(iota_id: Uuid) -> Option> { +pub async fn remove_rho(iota_id: i64) -> Option> { let mut connections = RHO_CONNECTIONS.write().await; connections.remove(&iota_id) } @@ -51,7 +50,7 @@ pub async fn add_rho(rho_connection: Arc) { } /// Get a RhoConnection by Iota ID directly -pub async fn get_rho_by_iota(iota_id: Uuid) -> Option> { +pub async fn get_rho_by_iota(iota_id: i64) -> Option> { let connections = RHO_CONNECTIONS.read().await; connections.get(&iota_id).map(Arc::clone) }