diff --git a/Cargo.lock b/Cargo.lock index b18750a..efc236d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -334,6 +334,31 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crossterm" version = "0.28.1" @@ -913,7 +938,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -1281,25 +1306,6 @@ dependencies = [ "libc", ] -[[package]] -name = "objc2-core-foundation" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" -dependencies = [ - "bitflags", -] - -[[package]] -name = "objc2-io-kit" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" -dependencies = [ - "libc", - "objc2-core-foundation", -] - [[package]] name = "object" version = "0.36.7" @@ -1534,6 +1540,26 @@ dependencies = [ "unicode-width 0.2.0", ] +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -1964,15 +1990,16 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.37.0" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" dependencies = [ + "cfg-if", + "core-foundation-sys", "libc", - "memchr", "ntapi", - "objc2-core-foundation", - "objc2-io-kit", + "once_cell", + "rayon", "windows", ] @@ -2531,24 +2558,21 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.61.3" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", + "windows-core 0.52.0", + "windows-targets 0.52.6", ] [[package]] -name = "windows-collections" -version = "0.2.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-core", + "windows-targets 0.52.6", ] [[package]] @@ -2564,17 +2588,6 @@ dependencies = [ "windows-strings", ] -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core", - "windows-link 0.1.3", - "windows-threading", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -2609,16 +2622,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core", - "windows-link 0.1.3", -] - [[package]] name = "windows-registry" version = "0.5.3" @@ -2717,15 +2720,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index fa20969..56095f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ cmake = "*" tokio = { version = "*", features = ["full"] } uuid = { version = "*", features = ["v4"] } walkdir = "2.5.0" -sysinfo = "*" +sysinfo = "0.30" serde = { version = "1.0.219", features = ["derive"] } ratatui = { version = "*", features = ["all-widgets"]} bytes = "*" diff --git a/src/auth/auth_connector.rs b/src/auth/auth_connector.rs index 21a677c..68ad0a6 100644 --- a/src/auth/auth_connector.rs +++ b/src/auth/auth_connector.rs @@ -1,4 +1,6 @@ +use crate::CONFIG; use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; +use crate::gui::log_panel::log_message; use crate::users::user_profile::UserProfile; use hex; use json::JsonValue; @@ -48,7 +50,7 @@ impl AuthConnector { .ok()?; let json = res.text().await.ok()?; let cv = CommunicationValue::from_json(&json); - Option::from(cv.is_type(CommunicationType::Success)) + Option::from(cv.is_type(CommunicationType::success)) } pub async fn get_uuid(username: &str) -> Option { @@ -60,10 +62,10 @@ impl AuthConnector { let res = client.get(&url).send().await.ok()?; let json = res.text().await.ok()?; let mut cv = CommunicationValue::from_json(&json); - if !cv.is_type(CommunicationType::Success) { + if !cv.is_type(CommunicationType::success) { return None; } - Uuid::parse_str(&*cv.get_data(DataTypes::UserId).unwrap().to_string()).ok() + Uuid::parse_str(&*cv.get_data(DataTypes::user_id).unwrap().to_string()).ok() } pub async fn get_user(user_id: Uuid) -> Option { @@ -73,31 +75,31 @@ impl AuthConnector { let json = res.text().await.ok()?; let mut cv = CommunicationValue::from_json(&json); - if cv.comm_type != CommunicationType::Success { + if cv.comm_type != CommunicationType::success { return None; } Some(AuthUser { created_at: cv - .get_data(DataTypes::CreatedAt) + .get_data(DataTypes::created_at) .unwrap() .to_string() .parse::() .unwrap_or(-1), - username: cv.get_data(DataTypes::Username).unwrap().to_string(), - display: cv.get_data(DataTypes::Display).unwrap().to_string(), - avatar: cv.get_data(DataTypes::Avatar).unwrap().to_string(), - about: cv.get_data(DataTypes::About).unwrap().to_string(), - status: cv.get_data(DataTypes::Status).unwrap().to_string(), - public_key: cv.get_data(DataTypes::PublicKey).unwrap().to_string(), + username: cv.get_data(DataTypes::username).unwrap().to_string(), + display: cv.get_data(DataTypes::display).unwrap().to_string(), + avatar: cv.get_data(DataTypes::avatar).unwrap().to_string(), + about: cv.get_data(DataTypes::about).unwrap().to_string(), + status: cv.get_data(DataTypes::status).unwrap().to_string(), + public_key: cv.get_data(DataTypes::public_key).unwrap().to_string(), sub_level: cv - .get_data(DataTypes::SubLevel) + .get_data(DataTypes::sub_level) .unwrap() .to_string() .parse::() .unwrap_or(-1), sub_end: cv - .get_data(DataTypes::SubEnd) + .get_data(DataTypes::sub_end) .unwrap() .to_string() .parse::() @@ -112,7 +114,7 @@ impl AuthConnector { let json = res.text().await.ok()?; let mut cv = CommunicationValue::from_json(&json); - Uuid::parse_str(&*cv.get_data(DataTypes::UserId).unwrap().to_string()).ok() + Uuid::parse_str(&*cv.get_data(DataTypes::user_id).unwrap().to_string()).ok() } pub async fn complete_register(user_profile: &UserProfile, iota_id: &str) -> bool { @@ -139,10 +141,10 @@ impl AuthConnector { let body = res.text().await.unwrap(); println!("Response body:\n{}", body); - CommunicationValue::from_json(&body).is_type(CommunicationType::Success) + CommunicationValue::from_json(&body).is_type(CommunicationType::success) } - pub async fn migrate_user(user_profile: &mut UserProfile, iota_id: &str) -> bool { + pub async fn migrate_user(user_profile: &mut UserProfile) -> bool { let url = format!( "https://auth.tensamin.methanium.net/api/change/iota-id/{}", user_profile.user_id @@ -150,7 +152,7 @@ impl AuthConnector { let client = Self::client(); let mut payload = JsonValue::new_object(); - payload["iota_id"] = iota_id.into(); + payload["iota_id"] = JsonValue::String(CONFIG.lock().unwrap().get_iota_id().to_string()); payload["reset_token"] = user_profile.reset_token.clone().into(); payload["new_token"] = user_profile.randomize_reset_token().into(); @@ -171,7 +173,7 @@ impl AuthConnector { match resp.text().await { Ok(text) => { let cv = CommunicationValue::from_json(&text); - cv.comm_type == CommunicationType::Success + cv.comm_type == CommunicationType::success } Err(_) => false, } diff --git a/src/data/communication.rs b/src/data/communication.rs index 98bdf50..a0e411e 100644 --- a/src/data/communication.rs +++ b/src/data/communication.rs @@ -1,6 +1,6 @@ use axum::Json; use json::number::Number; -use json::{JsonValue, object, parse, stringify}; +use json::{Array, JsonValue, object, parse, stringify}; use std::any::Any; use std::collections::HashMap; use std::env::VarsOs; @@ -8,73 +8,73 @@ use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; use uuid::Uuid; -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Eq, Hash, PartialEq, Clone, Debug)] pub enum DataTypes { - ErrorType, - ChatPartnerId, - IotaId, - UserId, - UserIds, - UserState, - UserStates, - UserPings, - CallState, - ScreenShare, - PrivateKeyHash, - Accepted, - AcceptedProfiles, - DeniedProfiles, - MessageContent, - MessageChunk, - SendTime, - GetTime, - GetVariant, - SharedSecretOwn, - SharedSecretOther, - SharedSecretSign, - SharedSecret, - CallId, - CallName, - CallSecretSha, - CallSecret, - SharedCallSecret, - StartDate, - EndDate, - ReceiverId, - SenderId, - Signature, - Signed, - Message, - LastPing, - PingIota, - PingClients, - Matches, - Omikron, - LoadedMessages, - MessageAmount, - Position, - Name, - Path, - Codec, - Function, - Payload, - Result, - Interactables, - WantToWatch, - Watcher, - CreatedAt, - Username, - Display, - Avatar, - About, - Status, - PublicKey, - SubLevel, - SubEnd, - CommunityAddress, - Challenge, - CommunityTitle, - Communities, + error_type, + chat_partner_id, + iota_id, + user_id, + user_ids, + user_state, + user_states, + user_pings, + call_state, + screen_share, + private_key_hash, + accepted, + accepted_profiles, + denied_profiles, + message_content, + message_chunk, + send_time, + get_time, + get_variant, + shared_secret_own, + shared_secret_other, + shared_secret_sign, + shared_secret, + call_id, + call_name, + call_secret_sha, + call_secret, + shared_call_secret, + start_date, + end_date, + receiver_id, + sender_id, + signature, + signed, + message, + last_ping, + ping_iota, + ping_clients, + matches, + omikron, + loaded_messages, + message_amount, + position, + name, + path, + codec, + function, + payload, + result, + interactables, + want_to_watch, + watcher, + created_at, + username, + display, + avatar, + about, + status, + public_key, + sub_level, + sub_end, + community_address, + challenge, + community_title, + communities, } impl DataTypes { @@ -83,209 +83,179 @@ impl DataTypes { let normalized = p0.to_lowercase().replace('_', ""); match normalized.as_str() { - "errortype" => DataTypes::ErrorType, - "chatpartnerid" => DataTypes::ChatPartnerId, - "iotaid" => DataTypes::IotaId, - "userid" => DataTypes::UserId, - "userids" => DataTypes::UserIds, - "userstate" => DataTypes::UserState, - "userstates" => DataTypes::UserStates, - "userpings" => DataTypes::UserPings, - "callstate" => DataTypes::CallState, - "screenshare" => DataTypes::ScreenShare, - "privatekeyhash" => DataTypes::PrivateKeyHash, - "accepted" => DataTypes::Accepted, - "acceptedprofiles" => DataTypes::AcceptedProfiles, - "deniedprofiles" => DataTypes::DeniedProfiles, - "messagecontent" => DataTypes::MessageContent, - "messagechunk" => DataTypes::MessageChunk, - "sendtime" => DataTypes::SendTime, - "gettime" => DataTypes::GetTime, - "getvariant" => DataTypes::GetVariant, - "sharedsecretown" => DataTypes::SharedSecretOwn, - "sharedsecretother" => DataTypes::SharedSecretOther, - "sharedsecretsign" => DataTypes::SharedSecretSign, - "sharedsecret" => DataTypes::SharedSecret, - "callid" => DataTypes::CallId, - "callname" => DataTypes::CallName, - "callsecretsha" => DataTypes::CallSecretSha, - "callsecret" => DataTypes::CallSecret, - "sharedcallsecret" => DataTypes::SharedCallSecret, - "startdate" => DataTypes::StartDate, - "enddate" => DataTypes::EndDate, - "receiverid" => DataTypes::ReceiverId, - "senderid" => DataTypes::SenderId, - "signature" => DataTypes::Signature, - "signed" => DataTypes::Signed, - "message" => DataTypes::Message, - "lastping" => DataTypes::LastPing, - "pingiota" => DataTypes::PingIota, - "pingclients" => DataTypes::PingClients, - "matches" => DataTypes::Matches, - "omikron" => DataTypes::Omikron, - "loadedmessages" => DataTypes::LoadedMessages, - "messageamount" => DataTypes::MessageAmount, - "position" => DataTypes::Position, - "name" => DataTypes::Name, - "path" => DataTypes::Path, - "codec" => DataTypes::Codec, - "function" => DataTypes::Function, - "payload" => DataTypes::Payload, - "result" => DataTypes::Result, - "interactables" => DataTypes::Interactables, - "wanttowatch" => DataTypes::WantToWatch, - "watcher" => DataTypes::Watcher, - "createdat" => DataTypes::CreatedAt, - "username" => DataTypes::Username, - "display" => DataTypes::Display, - "avatar" => DataTypes::Avatar, - "about" => DataTypes::About, - "status" => DataTypes::Status, - "publickey" => DataTypes::PublicKey, - "sublevel" => DataTypes::SubLevel, - "subend" => DataTypes::SubEnd, - "communityaddress" => DataTypes::CommunityAddress, - "challenge" => DataTypes::Challenge, - "communitytitle" => DataTypes::CommunityTitle, - "communities" => DataTypes::Communities, - _ => DataTypes::ErrorType, // fallback if unknown + "errortype" => DataTypes::error_type, + "chatpartnerid" => DataTypes::chat_partner_id, + "iotaid" => DataTypes::iota_id, + "userid" => DataTypes::user_id, + "userids" => DataTypes::user_ids, + "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, + "acceptedprofiles" => DataTypes::accepted_profiles, + "deniedprofiles" => DataTypes::denied_profiles, + "messagecontent" => DataTypes::message_content, + "messagechunk" => DataTypes::message_chunk, + "sendtime" => DataTypes::send_time, + "gettime" => DataTypes::get_time, + "getvariant" => DataTypes::get_variant, + "sharedsecretown" => DataTypes::shared_secret_own, + "sharedsecretother" => DataTypes::shared_secret_other, + "sharedsecretsign" => DataTypes::shared_secret_sign, + "sharedsecret" => DataTypes::shared_secret, + "callid" => DataTypes::call_id, + "callname" => DataTypes::call_name, + "callsecretsha" => DataTypes::call_secret_sha, + "callsecret" => DataTypes::call_secret, + "sharedcallsecret" => DataTypes::shared_call_secret, + "startdate" => DataTypes::start_date, + "enddate" => DataTypes::end_date, + "receiverid" => DataTypes::receiver_id, + "senderid" => DataTypes::sender_id, + "signature" => DataTypes::signature, + "signed" => DataTypes::signed, + "message" => DataTypes::message, + "lastping" => DataTypes::last_ping, + "pingiota" => DataTypes::ping_iota, + "pingclients" => DataTypes::ping_clients, + "matches" => DataTypes::matches, + "omikron" => DataTypes::omikron, + "loadedmessages" => DataTypes::loaded_messages, + "messageamount" => DataTypes::message_amount, + "position" => DataTypes::position, + "name" => DataTypes::name, + "path" => DataTypes::path, + "codec" => DataTypes::codec, + "function" => DataTypes::function, + "payload" => DataTypes::payload, + "result" => DataTypes::result, + "interactables" => DataTypes::interactables, + "wanttowatch" => DataTypes::want_to_watch, + "watcher" => DataTypes::watcher, + "createdat" => DataTypes::created_at, + "username" => DataTypes::username, + "display" => DataTypes::display, + "avatar" => DataTypes::avatar, + "about" => DataTypes::about, + "status" => DataTypes::status, + "publickey" => DataTypes::public_key, + "sublevel" => DataTypes::sub_level, + "subend" => DataTypes::sub_end, + "communityaddress" => DataTypes::community_address, + "challenge" => DataTypes::challenge, + "communitytitle" => DataTypes::community_title, + "communities" => DataTypes::communities, + _ => DataTypes::error_type, // fallback if unknown } } } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Clone, Debug)] pub enum CommunicationType { - Error, - Success, - Message, - MessageLive, - MessageOtherIota, - MessageChunk, - MessageGet, - ChangeConfirm, - ConfirmReceive, - ConfirmRead, - GetChats, - GetStates, - AddCommunity, - RemoveCommunity, - GetCommunities, - Challenge, - ChallengeResponse, - Register, - RegisterResponse, - Identification, - IdentificationResponse, - Ping, - Pong, - AddChat, - SendChat, - IotaConnected, - IotaClosed, - ClientChanged, - ClientConnected, - ClientClosed, - PublicKey, - PrivateKey, - WebrtcSdp, - WebrtcIce, - StartStream, - EndStream, - WatchStream, - GetCall, - NewCall, - CallInvite, - EndCall, - Function, - Update, + error, + success, + message, + message_live, + message_other_iota, + message_chunk, + message_get, + change_confirm, + confirm_receive, + confirm_read, + get_chats, + get_states, + add_community, + remove_community, + get_communities, + challenge, + challenge_response, + register, + register_response, + identification, + identification_response, + ping, + pong, + add_chat, + send_chat, + iota_connected, + iota_closed, + client_changed, + client_connected, + client_closed, + public_key, + private_key, + webrtc_sdp, + webrtc_ice, + start_stream, + end_stream, + watch_stream, + get_call, + new_call, + call_invite, + end_call, + function, + update, } impl CommunicationType { pub fn parse(p0: String) -> CommunicationType { let normalized = p0.to_lowercase().replace('_', ""); match normalized.as_str() { - "error" => CommunicationType::Error, - "success" => CommunicationType::Success, - "message" => CommunicationType::Message, - "messagelive" => CommunicationType::MessageLive, - "messageotheriota" => CommunicationType::MessageOtherIota, - "messagechunk" => CommunicationType::MessageChunk, - "messageget" => CommunicationType::MessageGet, - "changeconfirm" => CommunicationType::ChangeConfirm, - "confirmreceive" => CommunicationType::ConfirmReceive, - "confirmread" => CommunicationType::ConfirmRead, - "getchats" => CommunicationType::GetChats, - "getstates" => CommunicationType::GetStates, - "addcommunity" => CommunicationType::AddCommunity, - "removecommunity" => CommunicationType::RemoveCommunity, - "getcommunities" => CommunicationType::GetCommunities, - "challenge" => CommunicationType::Challenge, - "challengeresponse" => CommunicationType::ChallengeResponse, - "register" => CommunicationType::Register, - "registerresponse" => CommunicationType::RegisterResponse, - "identification" => CommunicationType::Identification, - "identificationresponse" => CommunicationType::IdentificationResponse, - "ping" => CommunicationType::Ping, - "pong" => CommunicationType::Pong, - "addchat" => CommunicationType::AddChat, - "sendchat" => CommunicationType::SendChat, - "iotaconnected" => CommunicationType::IotaConnected, - "iotaclosed" => CommunicationType::IotaClosed, - "clientchanged" => CommunicationType::ClientChanged, - "clientconnected" => CommunicationType::ClientConnected, - "clientclosed" => CommunicationType::ClientClosed, - "publickey" => CommunicationType::PublicKey, - "privatekey" => CommunicationType::PrivateKey, - "webrtcsdp" => CommunicationType::WebrtcSdp, - "webrtcice" => CommunicationType::WebrtcIce, - "startstream" => CommunicationType::StartStream, - "endstream" => CommunicationType::EndStream, - "watchstream" => CommunicationType::WatchStream, - "getcall" => CommunicationType::GetCall, - "newcall" => CommunicationType::NewCall, - "callinvite" => CommunicationType::CallInvite, - "endcall" => CommunicationType::EndCall, - "function" => CommunicationType::Function, - "update" => CommunicationType::Update, - _ => CommunicationType::Error, // fallback + "error" => CommunicationType::error, + "success" => CommunicationType::success, + "message" => CommunicationType::message, + "messagelive" => CommunicationType::message_live, + "messageotheriota" => CommunicationType::message_other_iota, + "messagechunk" => CommunicationType::message_chunk, + "messageget" => CommunicationType::message_get, + "changeconfirm" => CommunicationType::change_confirm, + "confirmreceive" => CommunicationType::confirm_receive, + "confirmread" => CommunicationType::confirm_read, + "getchats" => CommunicationType::get_chats, + "getstates" => CommunicationType::get_states, + "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, + "identificationresponse" => CommunicationType::identification_response, + "ping" => CommunicationType::ping, + "pong" => CommunicationType::pong, + "addchat" => CommunicationType::add_chat, + "sendchat" => CommunicationType::send_chat, + "iotaconnected" => CommunicationType::iota_connected, + "iotaclosed" => CommunicationType::iota_closed, + "clientchanged" => CommunicationType::client_changed, + "clientconnected" => CommunicationType::client_connected, + "clientclosed" => CommunicationType::client_closed, + "publickey" => CommunicationType::public_key, + "privatekey" => CommunicationType::private_key, + "webrtcsdp" => CommunicationType::webrtc_sdp, + "webrtcice" => CommunicationType::webrtc_ice, + "startstream" => CommunicationType::start_stream, + "endstream" => CommunicationType::end_stream, + "watchstream" => CommunicationType::watch_stream, + "getcall" => CommunicationType::get_call, + "newcall" => CommunicationType::new_call, + "callinvite" => CommunicationType::call_invite, + "endcall" => CommunicationType::end_call, + "function" => CommunicationType::function, + "update" => CommunicationType::update, + _ => CommunicationType::error, } } } -#[derive(Debug, Clone)] -pub enum LogLevel { - Important = 2, - Normal = 1, - Debug = 0, - None = -1, - DebugOnly = -2, -} #[derive(Debug, Clone)] -pub struct LogValue { - pub message: String, - pub log_level: LogLevel, -} - -impl LogValue { - pub fn new(message: impl Into, log_level: LogLevel) -> Self { - Self { - message: message.into(), - log_level, - } - } - - pub fn to_json(self) -> JsonValue { - object! { - message: self.message.clone(), - log_level: self.log_level as i32 - } - } -} -#[derive(Debug)] pub struct CommunicationValue { pub id: Uuid, pub comm_type: CommunicationType, - pub log_value: Option, pub sender: Uuid, pub receiver: Uuid, pub data: HashMap, @@ -296,7 +266,6 @@ impl CommunicationValue { Self { id: Uuid::new_v4(), comm_type, - log_value: None, sender: Uuid::new_v4(), receiver: Uuid::new_v4(), data: HashMap::new(), @@ -309,13 +278,6 @@ impl CommunicationValue { pub fn get_id(&self) -> Uuid { self.id.clone() } - pub fn with_log(mut self, log: LogValue) -> Self { - self.log_value = Some(log); - self - } - pub fn get_log(&self) -> &Option { - &self.log_value - } pub fn with_sender(mut self, sender: Uuid) -> Self { self.sender = sender; self @@ -342,6 +304,10 @@ impl CommunicationValue { self.data.insert(key, value); self } + pub fn add_array(mut self, key: DataTypes, value: Array) -> Self { + self.data.insert(key, JsonValue::Array(value)); + self + } pub fn get_data(&mut self, key: DataTypes) -> Option<&JsonValue> { self.data.get(&key) } @@ -360,7 +326,6 @@ impl CommunicationValue { type: format!("{:?}", self.comm_type), sender: self.sender.to_string(), receiver: self.receiver.to_string(), - log: self.log_value.as_ref().map(|l| l.clone().to_json()).unwrap_or(JsonValue::Null), data: jdata } } @@ -379,14 +344,6 @@ impl CommunicationValue { .and_then(|s| Uuid::parse_str(s).ok()) .unwrap_or(Uuid::new_v4()); - let log_value = if parsed["log"].is_object() { - Some(LogValue::new( - parsed["log"]["message"].as_str().unwrap_or("").to_string(), - LogLevel::Normal, - )) - } else { - None - }; 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() { @@ -399,24 +356,23 @@ impl CommunicationValue { Self { id: uuid, comm_type, - log_value, sender, receiver, data, } } pub fn ack_message(message_id: Uuid, sender: Uuid) -> CommunicationValue { - let mut cv = CommunicationValue::new(CommunicationType::Message).with_id(message_id); + let mut cv = CommunicationValue::new(CommunicationType::message).with_id(message_id); if let s = sender { - cv = cv.add_data(DataTypes::SenderId, JsonValue::String(s.to_string())); + cv = cv.add_data(DataTypes::send_time, JsonValue::String(s.to_string())); } cv } pub fn forward_to_other_iota(original: &mut CommunicationValue) -> CommunicationValue { let receiver = Uuid::from_str( &*original - .get_data(DataTypes::ReceiverId) + .get_data(DataTypes::receiver_id) .unwrap() .to_string(), ) @@ -428,15 +384,15 @@ impl CommunicationValue { .unwrap() .as_millis() as i64; - let cv = CommunicationValue::new(CommunicationType::MessageOtherIota) + let cv = CommunicationValue::new(CommunicationType::message_other_iota) .with_id(original.get_id()) .with_receiver(receiver.unwrap()) - .add_data(DataTypes::SendTime, JsonValue::String(now_ms.to_string())) + .add_data(DataTypes::send_time, JsonValue::String(now_ms.to_string())) .add_data( - DataTypes::MessageContent, + DataTypes::message_content, JsonValue::String( original - .get_data(DataTypes::MessageContent) + .get_data(DataTypes::message_content) .unwrap() .to_string(), ), @@ -444,6 +400,6 @@ impl CommunicationValue { // include sender_id if the original had one let sender = original.get_sender(); - cv.add_data(DataTypes::SenderId, JsonValue::String(sender.to_string())) + cv.add_data(DataTypes::sender_id, JsonValue::String(sender.to_string())) } } diff --git a/src/gui/log_panel.rs b/src/gui/log_panel.rs index e69de29..ac03d32 100644 --- a/src/gui/log_panel.rs +++ b/src/gui/log_panel.rs @@ -0,0 +1,405 @@ +use crate::langu::language_manager::from_key; +use crate::{APP_STATE, omikron::ping_pong_task::PingPongTask}; +use crossterm::{ + ExecutableCommand, + terminal::{LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, +}; +use ratatui::widgets::canvas::{Canvas, Line}; +use ratatui::{ + Terminal, + backend::CrosstermBackend, + layout::{Constraint, Direction, Layout}, + style::Color, + widgets::{Block, Borders, List, ListItem, Paragraph}, +}; +use std::{collections::VecDeque, io::stdout, process::Command, thread, time::Duration}; +use sysinfo::{RefreshKind, System}; + +const MAX_POINTS: usize = 1000; +const MAX_LOGS: usize = 100; + +#[derive(Clone)] +pub struct AppState { + logs: VecDeque, + cpu: Vec<(f64, f64)>, + ram: Vec<(f64, f64)>, + ping: Vec<(f64, f64)>, + net_up: Vec<(f64, f64)>, + net_down: Vec<(f64, f64)>, + sys_info: String, +} + +impl AppState { + pub fn new() -> Self { + Self { + logs: VecDeque::new(), + cpu: Vec::new(), + ram: Vec::new(), + ping: Vec::new(), + net_up: Vec::new(), + net_down: Vec::new(), + sys_info: String::from("Loading..."), + } + } + + pub fn push_log(&mut self, msg: String) { + if self.logs.len() >= MAX_LOGS { + self.logs.pop_front(); + } + self.logs.push_back(msg); + } + + pub fn push_cpu(&mut self, pt: (f64, f64)) { + self.cpu.push(pt); + if self.cpu.len() > MAX_POINTS { + self.cpu.remove(0); + } + } + + pub fn push_ram(&mut self, pt: (f64, f64)) { + self.ram.push(pt); + if self.ram.len() > MAX_POINTS { + self.ram.remove(0); + } + } + + pub fn push_ping_val(&mut self, pt: f64) { + self.ping.push((self.ping.len() as f64, pt)); + if self.ping.len() > MAX_POINTS { + self.ping.remove(0); + } + } + + pub fn push_net_up(&mut self, pt: (f64, f64)) { + self.net_up.push(pt); + if self.net_up.len() > MAX_POINTS { + self.net_up.remove(0); + } + } + + pub fn push_net_down(&mut self, pt: (f64, f64)) { + self.net_down.push(pt); + if self.net_down.len() > MAX_POINTS { + self.net_down.remove(0); + } + } +} + +pub fn log_message_trans(key: impl Into) { + APP_STATE.lock().unwrap().push_log(from_key(&key.into())); +} +pub fn log_message(msg: impl Into) { + APP_STATE.lock().unwrap().push_log(msg.into()); +} +fn smooth_data(data: &[(f64, f64)], window_size: usize) -> Vec<(f64, f64)> { + if data.len() < window_size { + return data.to_vec(); + } + let mut smoothed = Vec::with_capacity(data.len()); + for i in 0..data.len() { + let start = if i + 1 >= window_size { + i + 1 - window_size + } else { + 0 + }; + let window = &data[start..=i]; + let avg = window.iter().map(|(_, y)| y).sum::() / window.len() as f64; + smoothed.push((data[i].0, avg)); + } + smoothed +} + +fn downsample_to_fit_width(data: &[(f64, f64)], width: u16) -> Vec<(f64, f64)> { + let width_usize = (width as usize) * 2; + let len = data.len(); + if len == 0 { + return Vec::new(); + } + let slice = if len <= width_usize { + data.to_vec() + } else { + data[len - width_usize..].to_vec() + }; + slice +} + +fn measure_ping_ms(host: &str) -> Option { + // adapt for Windows + let output = Command::new("ping") + .arg("-c") + .arg("1") + .arg("-W") + .arg("1") + .arg(host) + .output() + .ok()?; + let out = String::from_utf8_lossy(&output.stdout); + for line in out.lines() { + if line.contains("time=") { + if let Some(idx) = line.find("time=") { + let substr = &line[idx + 5..]; + if let Some(end) = substr.find(" ms") { + let num = &substr[..end]; + if let Ok(f) = num.parse::() { + return Some(f); + } + } + } + } + } + None +} + +pub fn setup() { + enable_raw_mode().unwrap(); + + // Start a background thread to sample metrics + thread::spawn(move || { + let mut sys = System::new_with_specifics(RefreshKind::new()); + let mut last_total_received = 0u64; + let mut last_total_transmitted = 0u64; + let mut counter = 0.0; + loop { + sys.refresh_all(); + + let mut tcpu = 0; + for cpu in sys.cpus() { + tcpu += cpu.cpu_usage() as i64; + tcpu /= 2; + } + let ram = (sys.used_memory() as f64 / sys.total_memory() as f64) * 100.0; + + let mut total_received = 0u64; + let mut total_transmitted = 0u64; + + let delta_received = if last_total_received == 0 { + 0 + } else { + total_received.saturating_sub(last_total_received) + }; + let delta_transmitted = if last_total_transmitted == 0 { + 0 + } else { + total_transmitted.saturating_sub(last_total_transmitted) + }; + last_total_received = total_received; + last_total_transmitted = total_transmitted; + + let net_down = delta_received as f64; + let net_up = delta_transmitted as f64; + + { + let mut st = APP_STATE.lock().unwrap(); + st.push_cpu((counter, tcpu as f64)); + st.push_ram((counter, ram)); + st.push_net_down((counter, net_down)); + st.push_net_up((counter, net_up)); + + st.sys_info = format!( + "CPU: {:.1}% RAM: {:.1}%\nNetDown: {} NetUp: {}", + tcpu, ram, delta_received, delta_transmitted + ); + } + + counter += 1.0; + thread::sleep(Duration::from_millis(250)); + } + }); + + // UI rendering loop in a separate thread + thread::spawn(move || { + let stdout = stdout(); + let mut terminal = Terminal::new(CrosstermBackend::new(stdout)).unwrap(); + + enable_raw_mode().unwrap(); + + loop { + terminal + .draw(|f| { + let size = f.size(); + let chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(40), Constraint::Percentage(60)]) + .split(size); + + let left = chunks[0]; + let right = chunks[1]; + + { + let st = APP_STATE.lock().unwrap(); + let items: Vec = st + .logs + .iter() + .rev() + .map(|s| ListItem::new(s.clone())) + .collect(); + let list = List::new(items) + .block(Block::default().title("Logs").borders(Borders::ALL)); + f.render_widget(list, left); + } + + let right_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Length(5), Constraint::Min(0)]) + .split(right); + + { + let st = APP_STATE.lock().unwrap(); + let header = Paragraph::new(st.sys_info.clone()) + .block(Block::default().title("System Info").borders(Borders::ALL)); + f.render_widget(header, right_chunks[0]); + } + + let grid_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(right_chunks[1]); + + let left_column = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(grid_chunks[0]); + + let right_column = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(grid_chunks[1]); + + let st = APP_STATE.lock().unwrap(); + let w = grid_chunks[0].width.saturating_sub(2); + + let ping_ds = downsample_to_fit_width(&smooth_data(&st.ping, 3), w); + let cpu_ds = downsample_to_fit_width(&smooth_data(&st.cpu, 3), w); + let ram_ds = downsample_to_fit_width(&smooth_data(&st.ram, 3), w); + let down_ds = downsample_to_fit_width(&st.net_down, w); + let up_ds = downsample_to_fit_width(&st.net_up, w); + + // ---- Render CPU ---- + { + let min_x = cpu_ds.first().map(|(x, _)| *x).unwrap_or(0.0); + let max_x = cpu_ds.last().map(|(x, _)| *x).unwrap_or(100.0); + + let canvas = Canvas::default() + .block(Block::default().title("CPU %").borders(Borders::ALL)) + .x_bounds([min_x, max_x]) + .y_bounds([0.0, 100.0]) + .paint(|ctx| { + for (x, y) in &cpu_ds { + ctx.draw(&Line { + x1: *x, + y1: 0.0, + x2: *x, + y2: *y, + color: Color::Cyan, + }); + } + }); + f.render_widget(canvas, left_column[0]); + } + + // ---- Render RAM ---- + { + let min_x = ram_ds.first().map(|(x, _)| *x).unwrap_or(0.0); + let max_x = ram_ds.last().map(|(x, _)| *x).unwrap_or(100.0); + let canvas = Canvas::default() + .block(Block::default().title("RAM %").borders(Borders::ALL)) + .x_bounds([min_x, max_x]) + .y_bounds([0.0, 100.0]) + .paint(|ctx| { + for (x, y) in &ram_ds { + ctx.draw(&Line { + x1: *x, + y1: 0.0, + x2: *x, + y2: *y, + color: Color::Magenta, + }); + } + }); + f.render_widget(canvas, left_column[1]); + } + + // ---- Render Ping ---- + { + let min_x = ping_ds.first().map(|(x, _)| *x).unwrap_or(0.0); + let max_x = ping_ds.last().map(|(x, _)| *x).unwrap_or(100.0); + let max_y = ping_ds.iter().map(|(_, y)| *y).fold(1.0, f64::max); + + let canvas = Canvas::default() + .block( + Block::default() + .title(format!("Ping {}(ms)", max_y)) + .borders(Borders::ALL), + ) + .x_bounds([min_x, max_x]) + .y_bounds([0.0, max_y]) + .paint(|ctx| { + for (x, y) in &ping_ds { + ctx.draw(&Line { + x1: *x, + y1: 0.0, + x2: *x, + y2: *y, + color: Color::Yellow, + }); + } + }); + f.render_widget(canvas, right_column[0]); + } + + // ---- Render Network ---- + { + let min_x = up_ds.first().map(|(x, _)| *x).unwrap_or(0.0); + let max_x = up_ds.last().map(|(x, _)| *x).unwrap_or(100.0); + let mut max_sum: f64 = 1.0; + for ((_, up), (_, down)) in up_ds.iter().zip(down_ds.iter()) { + max_sum = max_sum.max(up + down); + } + + let canvas = Canvas::default() + .block( + Block::default() + .title("Network Up/Down") + .borders(Borders::ALL), + ) + .x_bounds([min_x, max_x]) + .y_bounds([0.0, max_sum]) + .paint(|ctx| { + for (x, dval) in &down_ds { + ctx.draw(&Line { + x1: *x, + y1: 0.0, + x2: *x, + y2: *dval, + color: Color::Red, + }); + } + for (x, uval) in &up_ds { + let dval = down_ds + .iter() + .find(|(xx, _)| (*xx - *x).abs() < f64::EPSILON) + .map(|(_, y)| *y) + .unwrap_or(0.0); + + ctx.draw(&Line { + x1: *x, + y1: dval, + x2: *x, + y2: dval + *uval, + color: Color::Green, + }); + } + }); + + f.render_widget(canvas, right_column[1]); + } + }) + .unwrap(); + + thread::sleep(Duration::from_millis(100)); + } + }); + // Clean up terminal before exit + disable_raw_mode().unwrap(); + stdout().execute(LeaveAlternateScreen).unwrap(); +} diff --git a/src/gui/mod.rs b/src/gui/mod.rs index a7ae7f3..cdd1499 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1 +1,2 @@ -pub mod ratatui_interface; \ No newline at end of file +pub mod log_panel; +pub mod ratatui_interface; diff --git a/src/langu/language_creator.rs b/src/langu/language_creator.rs index 619542a..9e62769 100644 --- a/src/langu/language_creator.rs +++ b/src/langu/language_creator.rs @@ -9,19 +9,15 @@ pub fn create_languages() { let mut debug_messages = JsonValue::new_object(); // FRONTEND - frontend_messages.insert( - "USER_CONTEXT_GET_CONVERSATIONS", - "User {} is loading conversations", - ); - frontend_messages.insert( - "USER_CONTEXT_GET_COMMUNITIES", - "User {} is loading communities", - ); - frontend_messages.insert("ADD_CONVERSATION", "User {} added {}"); + frontend_messages.insert("GET_CHATS", "User {} is loading conversations"); + frontend_messages.insert("MESSAGE_GET", "User {} is loading messages"); + frontend_messages.insert("get_communities", "User {} is loading communities"); + frontend_messages.insert("client_connected", "Client {} connected"); + frontend_messages.insert("add_conversation", "User {} added {}"); // OMIKRON omikron_messages.insert( - "IdentificationResponse", + "identification_response", "IOTA identified on Omikron, {} users!", ); @@ -32,6 +28,7 @@ pub fn create_languages() { general_texts.insert("IOTA_ID", "IOTA ID: {}-####-####-####-############"); general_texts.insert("USER_ID", "USER ID: {}"); general_texts.insert("USER_IDS", "USER IDS: {}"); + general_texts.insert("SETUP_COMPLETED", "Launched"); // DEBUG debug_messages.insert("", ""); @@ -55,4 +52,9 @@ pub fn create_languages() { "debug.json", &debug_messages.to_string(), ); + save_file( + "languages/en_INT", + "general.json", + &general_texts.to_string(), + ); } diff --git a/src/langu/language_manager.rs b/src/langu/language_manager.rs index bf6a0f6..ca945f1 100644 --- a/src/langu/language_manager.rs +++ b/src/langu/language_manager.rs @@ -32,9 +32,7 @@ pub fn from_key(key: &str) -> String { LANGUAGE_PACK .lock() .unwrap() - .language - .get(key) - .unwrap_or(&String::new()) + .get_translation(key) .to_string() } @@ -90,9 +88,18 @@ impl LanguagePack { self.language .insert(key.to_string(), value.as_str().unwrap().to_string()); } - } - pub fn get_translation(&self, key: &str) -> &String { - self.language.get(key).unwrap() + let general_messages = file_util::load_file(&path, "general.json"); + let general_messages = parse(&general_messages).unwrap(); + for (key, value) in general_messages.entries() { + self.language + .insert(key.to_string(), value.as_str().unwrap().to_string()); + } + } + pub fn get_translation(&self, key: &str) -> String { + match self.language.get(key) { + Some(v) if !v.is_empty() => v.clone(), + _ => key.to_uppercase(), + } } } diff --git a/src/main.rs b/src/main.rs index 4bb5431..854c988 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,41 +1,30 @@ -use base64::Engine; -use base64::engine::general_purpose; -use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; -use futures_util::SinkExt; use json::JsonValue::String; -use json::{self, JsonValue}; -use rand::Rng; -use rand_core::OsRng; -use rand_core::RngCore; -use reqwest::header::PUBLIC_KEY_PINS_REPORT_ONLY; -use sha2::{Digest, Sha256}; +use json::{self}; use std::sync::Arc; +use std::sync::LazyLock; +use std::sync::Mutex; use uuid::Uuid; -use x448::{PublicKey, Secret}; +mod auth; mod data; mod eula; +mod gui; +mod langu; mod omikron; mod users; mod util; -mod gui { - pub mod ratatui_interface; -} -mod auth; -mod langu; -use crate::auth::auth_connector::AuthConnector; use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; -use crate::eula::*; +use crate::gui::log_panel; +use crate::gui::log_panel::{AppState, log_message, log_message_trans}; use crate::langu::language_creator; -use crate::langu::language_manager; use crate::omikron::omikron_connection::OmikronConnection; use crate::omikron::ping_pong_task::PingPongTask; use crate::users::user_manager::UserManager; -use crate::users::user_profile::UserProfile; -use crate::users::user_profile_full::UserProfileFull; -use crate::util::config_util::{self, CONFIG, ConfigUtil}; -use crate::util::file_util; +use crate::util::config_util::CONFIG; + +pub static APP_STATE: LazyLock>> = + LazyLock::new(|| Arc::new(Mutex::new(AppState::new()))); #[tokio::main] async fn main() { @@ -46,8 +35,8 @@ async fn main() { //} // UI - gui::ratatui_interface::launch(false); + log_panel::setup(); // LANGUAGE PACK language_creator::create_languages(); @@ -68,9 +57,10 @@ async fn main() { if !sb.is_empty() { sb.remove(0); + sb = sb + ","; } - println!( - "IOTA ID: {}-####-####-####-############", + log_message(format!( + "IOTA ID: {}-####-####-####-############", CONFIG .lock() .unwrap() @@ -79,8 +69,8 @@ async fn main() { .split("-") .next() .unwrap() - ); - println!("User ID: {}", sb); + )); + log_message(format!("User IDS: {}", sb)); // IDENTIFICATION ON OMIKRON let omikron: OmikronConnection = OmikronConnection::new(); @@ -88,10 +78,10 @@ async fn main() { let _ping_pong_task = PingPongTask::new(Arc::new(OmikronConnection::new())); omikron .send_message( - CommunicationValue::new(CommunicationType::Identification) - .add_data(DataTypes::UserIds, String(sb.to_string())) + CommunicationValue::new(CommunicationType::identification) + .add_data(DataTypes::user_ids, String(sb.to_string())) .add_data( - DataTypes::IotaId, + DataTypes::iota_id, String(CONFIG.lock().unwrap().get_iota_id().to_string()), ) .to_json() @@ -100,6 +90,7 @@ async fn main() { .to_string(), ) .await; + log_message_trans("SETUP_COMPLETED"); loop {} } diff --git a/src/omikron/omikron_connection.rs b/src/omikron/omikron_connection.rs index b52ebbf..e8b23be 100644 --- a/src/omikron/omikron_connection.rs +++ b/src/omikron/omikron_connection.rs @@ -1,5 +1,8 @@ +use crate::APP_STATE; use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; -use crate::omikron::ping_pong_task::PingPongTask; +use crate::gui::log_panel::log_message; +use crate::gui::log_panel::log_message_trans; +use crate::omikron::ping_pong_task::{self, PingPongTask}; use crate::users::contact::Contact; use crate::users::user_community_util::UserCommunityUtil; use crate::util::chat_files::ChatFiles; @@ -8,6 +11,7 @@ use futures_util::{SinkExt, StreamExt}; use json::JsonValue; use json::number::Number; use std::collections::HashMap; +use std::pin::Pin; use std::str::FromStr; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; @@ -31,8 +35,9 @@ pub struct OmikronConnection { >, >, >, - waiting: Arc>>>, // waiting for responses - pingpong: Arc>>>, // ping-pong handler + waiting: Arc>>>, // waiting for responses + pingpong: Arc>>>, // ping-pong handler + ping_pong_task: Arc>>>, } impl OmikronConnection { @@ -41,18 +46,27 @@ impl OmikronConnection { writer: Arc::new(Mutex::new(None)), waiting: Arc::new(Mutex::new(HashMap::new())), pingpong: Arc::new(Mutex::new(None)), + ping_pong_task: Arc::new(Mutex::new(None)), } } /// Connect loop with retry - pub async fn connect<'a>(&'a self) { + pub async fn connect(&self) { loop { match connect_async("wss://tensamin.methanium.net/ws/iota/").await { Ok((ws_stream, _)) => { let (write_half, read_half) = ws_stream.split(); *self.writer.lock().await = Some(write_half); - self.spawn_listener(read_half); - self.start_ping_pong_task().await; + self.spawn_listener(read_half).await; + + let ppt = PingPongTask::new(Arc::new(self.clone())); + *self.ping_pong_task.lock().await = Some(Arc::new(ppt.clone())); + tokio::spawn(async move { + loop { + ppt.send_ping(); + sleep(Duration::from_secs(5)).await; + } + }); break; } Err(e) => { @@ -61,18 +75,6 @@ impl OmikronConnection { } } } - - // Start the PingPongTask - pub async fn start_ping_pong_task(&self) { - let ping_pong_task = PingPongTask::new(Arc::new(self.clone())); - - // Store the task handle in `pingpong` so we can manage it - let mut pingpong_handle = self.pingpong.lock().await; - *pingpong_handle = Some(tokio::spawn(async move { - ping_pong_task.run_ping_loop(); - })); - } - pub async fn send_message(&self, msg: String) { Self::send_message_static(&self.writer, msg).await; } @@ -87,7 +89,7 @@ impl OmikronConnection { } /// Listener for all incoming messages - fn spawn_listener( + async fn spawn_listener( &self, mut read_half: futures_util::stream::SplitStream< WebSocketStream>, @@ -95,57 +97,60 @@ impl OmikronConnection { ) { let waiting = self.waiting.clone(); let writer = self.writer.clone(); - + let sel = self.clone(); tokio::spawn(async move { while let Some(msg) = read_half.next().await { match msg { Ok(Message::Close(Some(frame))) => { - println!("[Omikron] Closed: {:?}", frame); + log_message(format!("[Omikron] Closed: {:?}", frame)); break; } Ok(Message::Text(text)) => { let mut cv = CommunicationValue::from_json(&text); // needs CommunicationValue parser - if cv.is_type(CommunicationType::Pong) { - // handle pingpong reset here - continue; + if cv.is_type(CommunicationType::pong) { + sel.ping_pong_task + .lock() + .await + .as_mut() + .unwrap() + .handle_pong(&cv, true); + return; } // ************************************************ // // Direct messages // // ************************************************ // - println!("[Omikron] Received message: {:?}", cv); - if cv.is_type(CommunicationType::MessageOtherIota) { + + log_message_trans(format!("{:?}", &cv.comm_type)); + if cv.is_type(CommunicationType::message_other_iota) { let sender_id = &cv.get_sender(); let receiver_id = &cv.get_receiver(); ChatFiles::add_message( - cv.get_data(DataTypes::SendTime) + cv.get_data(DataTypes::send_time) .unwrap() .as_i64() .unwrap_or(-1), false, *receiver_id, *sender_id, - cv.get_data(DataTypes::MessageContent) + cv.get_data(DataTypes::message_content) .unwrap() .as_str() .unwrap(), - false, - false, - 0, ); - let response = CommunicationValue::new(CommunicationType::MessageLive) + let response = CommunicationValue::new(CommunicationType::message_live) .with_id(cv.get_id()) .with_receiver(cv.get_receiver()) .add_data( - DataTypes::SendTime, - cv.get_data(DataTypes::SendTime).unwrap().clone(), + DataTypes::send_time, + cv.get_data(DataTypes::send_time).unwrap().clone(), ) .add_data( - DataTypes::Message, - cv.get_data(DataTypes::MessageContent).unwrap().clone(), + DataTypes::message, + cv.get_data(DataTypes::message_content).unwrap().clone(), ) .add_data( - DataTypes::SenderId, + DataTypes::sender_id, JsonValue::String(cv.get_sender().clone().to_string()), ); Self::send_message_static( @@ -156,26 +161,24 @@ impl OmikronConnection { continue; } - if cv.is_type(CommunicationType::Message) { - /* - "user": { - "avatar": true, - "display": true, - "timestamp": true - }, - "body": { - "tint": "", - "content": "", - "files": [ - { - "name": "", - "id": "", - "type": "[ image | image_top_right | file ]" - } - ] - } + if cv.is_type(CommunicationType::message) { + /* DATA CONTAINER: + "sent_by_self": true, + "timestamp": unixTimestamp, + "files": [ // wenn keine files dann weglassen + { + "name": "", + "id": "", + "type": "[ image | image_top_right | file ]" + } + ], + "content": "" */ let my_id = cv.get_sender(); + let other_id = Uuid::from_str( + &*cv.get_data(DataTypes::receiver_id).unwrap().to_string(), + ) + .unwrap(); ChatFiles::add_message( SystemTime::now() .duration_since(UNIX_EPOCH) @@ -183,20 +186,12 @@ impl OmikronConnection { .as_millis() as i64, true, my_id, - Uuid::from_str( - &*cv.get_data(DataTypes::ReceiverId).unwrap().to_string(), - ) - .unwrap(), - &*cv.get_data(DataTypes::MessageContent).unwrap().to_string(), - false, - false, - 0, + other_id, + &*cv.get_data(DataTypes::message_content).unwrap().to_string(), ); - // ack let ack = CommunicationValue::ack_message(cv.get_id(), my_id); Self::send_message_static(&writer.clone(), ack.to_json().to_string()) .await; - // forward let forward = CommunicationValue::forward_to_other_iota(&mut cv); Self::send_message_static( &writer.clone(), @@ -206,55 +201,52 @@ impl OmikronConnection { continue; } - if cv.is_type(CommunicationType::MessageGet) { + if cv.is_type(CommunicationType::message_get) { let my_id = cv.get_sender(); let partner_id = Uuid::from_str( - &*cv.get_data(DataTypes::ChatPartnerId).unwrap().to_string(), + &*cv.get_data(DataTypes::user_id).unwrap().to_string(), ) .unwrap(); let offset = cv - .get_data(DataTypes::LoadedMessages) + .get_data(DataTypes::loaded_messages) .unwrap_or(&JsonValue::Null) .to_string() .parse::() .unwrap_or(0); let amount = cv - .get_data(DataTypes::MessageAmount) + .get_data(DataTypes::message_amount) .unwrap_or(&JsonValue::Null) .to_string() .parse::() .unwrap_or(0); - let messages = - ChatFiles::get_messages(my_id, partner_id, offset, amount); // needs ChatFiles - let mut resp = CommunicationValue::new(CommunicationType::MessageChunk) + ChatFiles::get_messages(my_id, partner_id, offset, amount); + let resp = CommunicationValue::new(CommunicationType::message_chunk) .with_id(cv.get_id()) - .with_receiver(my_id); - if !messages.is_empty() { - resp = resp.add_data(DataTypes::MessageChunk, messages); - } + .with_receiver(my_id) + .add_data(DataTypes::message_chunk, messages); + Self::send_message_static(&writer.clone(), resp.to_json().to_string()) .await; continue; } - if cv.is_type(CommunicationType::GetChats) { + if cv.is_type(CommunicationType::get_chats) { let user_id = cv.get_sender(); - let users = get_users(user_id); // needs ChatsUtil - let resp = CommunicationValue::new(CommunicationType::GetChats) + let users = get_users(user_id); + let resp = CommunicationValue::new(CommunicationType::get_chats) .with_id(cv.get_id()) .with_receiver(user_id) - .add_data(DataTypes::UserIds, users); - println!("ALARM: {}", resp.to_json().to_string()); + .add_data(DataTypes::user_ids, users); Self::send_message_static(&writer.clone(), resp.to_json().to_string()) .await; continue; } - if cv.is_type(CommunicationType::AddChat) { + if cv.is_type(CommunicationType::add_chat) { let user_id = cv.get_sender(); let other_id = Uuid::from_str( - &*cv.get_data(DataTypes::UserId).unwrap().to_string(), + &*cv.get_data(DataTypes::user_id).unwrap().to_string(), ) .unwrap(); let mut contact = @@ -266,7 +258,7 @@ impl OmikronConnection { .as_millis() as i64, ); mod_user(user_id, &contact); - let resp = CommunicationValue::new(CommunicationType::AddChat) + let resp = CommunicationValue::new(CommunicationType::add_chat) .with_id(cv.get_id()) .with_receiver(user_id); Self::send_message_static(&writer.clone(), resp.to_json().to_string()) @@ -274,16 +266,16 @@ impl OmikronConnection { continue; } - if cv.is_type(CommunicationType::AddCommunity) { + if cv.is_type(CommunicationType::add_community) { UserCommunityUtil::add_community( cv.get_sender(), - cv.get_data(DataTypes::CommunityAddress) + cv.get_data(DataTypes::community_address) .unwrap() .to_string(), - cv.get_data(DataTypes::CommunityTitle).unwrap().to_string(), - cv.get_data(DataTypes::Position).unwrap().to_string(), + cv.get_data(DataTypes::community_title).unwrap().to_string(), + cv.get_data(DataTypes::position).unwrap().to_string(), ); - let resp = CommunicationValue::new(CommunicationType::AddCommunity) + let resp = CommunicationValue::new(CommunicationType::add_community) .with_id(cv.get_id()) .with_receiver(cv.get_sender()); Self::send_message_static(&writer.clone(), resp.to_json().to_string()) @@ -291,12 +283,12 @@ impl OmikronConnection { continue; } - if cv.is_type(CommunicationType::GetCommunities) { - let resp = CommunicationValue::new(CommunicationType::GetCommunities) + if cv.is_type(CommunicationType::get_communities) { + let resp = CommunicationValue::new(CommunicationType::get_communities) .with_id(cv.get_id()) .with_receiver(cv.get_sender()) - .add_data( - DataTypes::Communities, + .add_array( + DataTypes::communities, UserCommunityUtil::get_communities(cv.get_sender()), ); // needs UserCommunityUtil Self::send_message_static(&writer.clone(), resp.to_json().to_string()) @@ -304,14 +296,14 @@ impl OmikronConnection { continue; } - if cv.is_type(CommunicationType::RemoveCommunity) { + if cv.is_type(CommunicationType::remove_community) { UserCommunityUtil::remove_community( cv.get_sender(), - cv.get_data(DataTypes::CommunityAddress) + cv.get_data(DataTypes::community_address) .unwrap() .to_string(), ); // needs UserCommunityUtil - let resp = CommunicationValue::new(CommunicationType::RemoveCommunity) + let resp = CommunicationValue::new(CommunicationType::remove_community) .with_id(cv.get_id()) .with_receiver(cv.get_sender()); Self::send_message_static(&writer.clone(), resp.to_json().to_string()) @@ -320,7 +312,7 @@ impl OmikronConnection { } } Err(e) => { - eprintln!("[Omikron] Error: {}", e); + log_message(format!("[Omikron] Error: {}", e)); break; } _ => {} @@ -351,23 +343,11 @@ impl OmikronConnection { } } - pub fn on_answer(&self, message_id: Uuid, callback: F) - where - F: Fn(CommunicationValue) + Send + 'static, - { - tokio::spawn({ - let waiting = self.waiting.clone(); - async move { - waiting.lock().await.insert(message_id, Box::new(callback)); - } - }); - } - pub async fn send_ping_message(&self, uuid: Uuid) { // Send the ping message over the connection - let ping_message = CommunicationValue::new(CommunicationType::Ping) + let ping_message = CommunicationValue::new(CommunicationType::ping) .with_id(uuid) - .add_data_num(DataTypes::LastPing, Number::from(2)) + .add_data_num(DataTypes::last_ping, Number::from(2)) .to_json() .to_string(); self.send_message(ping_message).await; diff --git a/src/omikron/ping_pong_task.rs b/src/omikron/ping_pong_task.rs index 21fa900..4d6a140 100644 --- a/src/omikron/ping_pong_task.rs +++ b/src/omikron/ping_pong_task.rs @@ -1,7 +1,10 @@ +use crate::APP_STATE; use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; +use crate::gui::log_panel::AppState; use crate::omikron::omikron_connection::OmikronConnection; use color_eyre::owo_colors::OwoColorize; use futures_util::{SinkExt, StreamExt}; +use std::arch::x86_64::_SIDD_MASKED_NEGATIVE_POLARITY; use std::collections::HashMap; use std::sync::Arc; use tokio::sync::Mutex; @@ -10,12 +13,22 @@ use uuid::Uuid; #[derive(Clone)] pub struct PingPongTask { - pub parent: Arc, // assuming OmikronConnection is your connection type + pub parent: Arc, pub message_send_times: Arc>>, pub no_ping_in: Arc>, pub last_ping: Arc>>, } +fn assert_send_sync() {} +#[test] +fn check_omikron_connection_send_sync() { + assert_send_sync::(); +} + +#[test] +fn check_pingpong_task_send_sync() { + assert_send_sync::(); +} impl PingPongTask { pub fn new(parent: Arc) -> Self { let message_send_times = Arc::new(Mutex::new(HashMap::new())); @@ -29,50 +42,32 @@ impl PingPongTask { last_ping: last_ping.clone(), }; - // Spawn the periodic ping task - tokio::spawn({ - let task = task.clone(); // Clone the task (Arc) so that it lives long enough for the async task - async move { - task.run_ping_loop().await; - } - }); - task } - pub async fn run_ping_loop(&self) { - loop { - sleep(Duration::from_secs(5)).await; - self.send_ping().await; - } - } - - pub async fn send_ping(&self) { - let uuid = Uuid::new_v4(); - let send_time = Instant::now(); - - { - let mut message_send_times = self.message_send_times.lock().await; + pub fn send_ping(&self) { + let sel = self.clone(); + tokio::spawn(async move { + let uuid = Uuid::new_v4(); + let send_time = Instant::now(); + let mut message_send_times = sel.message_send_times.lock().await; message_send_times.insert(uuid, send_time); - } - let no_ping_in = { - let no_ping_in = self.no_ping_in.lock().await; - *no_ping_in - }; + let no_ping_in_val = { + let no_ping_in = sel.no_ping_in.lock().await; + *no_ping_in + }; - if no_ping_in != -1 { - // Connection slow or disconnected - self.handle_slow_connection(no_ping_in).await; - } else { - // Connection is fine - self.parent.send_ping_message(uuid).await; - } + if no_ping_in_val != -1 { + sel.handle_slow_connection(no_ping_in_val).await; + } else { + sel.parent.send_ping_message(uuid).await; + } + }); } pub async fn handle_slow_connection(&self, no_ping_in: i32) { if no_ping_in > 8 { - // Attempt reconnection if ping times out self.parent.reconnect().await; self.reconnect().await; } @@ -80,37 +75,39 @@ impl PingPongTask { pub async fn reconnect(&self) { let mut no_ping_in = self.no_ping_in.lock().await; - *no_ping_in = -1; // Reset slow count + *no_ping_in = -1; } - pub async fn handle_pong(&self, cv: &CommunicationValue) { - let send_time = { - let message_send_times = self.message_send_times.lock().await; - message_send_times.get(&cv.get_id()).cloned() - }; + pub fn handle_pong(&self, cv: &CommunicationValue, log: bool) { + let sel = self.clone(); + let cv = cv.clone(); + tokio::spawn(async move { + let send_time = { + let message_send_times = sel.message_send_times.lock().await; + message_send_times.get(&cv.get_id()).cloned() + }; - if let Some(send_time) = send_time { - let receive_time = Instant::now(); - let ping = receive_time.duration_since(send_time).as_millis() as u64; + if let Some(send_time) = send_time { + let receive_time = Instant::now(); + let ping = receive_time.duration_since(send_time).as_millis() as u64; - { - let mut last_ping = self.last_ping.lock().await; + let mut last_ping = sel.last_ping.lock().await; *last_ping = Some(ping); - } - - { - let mut no_ping_in = self.no_ping_in.lock().await; + let mut no_ping_in = sel.no_ping_in.lock().await; *no_ping_in = -1; + let mut message_send_times = sel.message_send_times.lock().await; + message_send_times.remove(&cv.get_id()); + + if log { + APP_STATE.lock().unwrap().push_log("12.0".to_string()); + APP_STATE.lock().unwrap().push_ping_val(12.0); + } } - } + }); } pub async fn cancel(&self) { - // Cancel or stop the task let mut no_ping_in = self.no_ping_in.lock().await; - *no_ping_in = -1; // Reset the counter - - // Example of stopping the ping task gracefully - // If using a task join handle or similar + *no_ping_in = -1; } } diff --git a/src/users/user_community_util.rs b/src/users/user_community_util.rs index d0764ed..f9e1d95 100644 --- a/src/users/user_community_util.rs +++ b/src/users/user_community_util.rs @@ -1,4 +1,5 @@ -use json::{self, JsonValue}; +use crate::util::file_util::save_file; +use json::{self, Array, JsonValue}; use std::fs; use std::path::Path; use uuid::Uuid; @@ -7,8 +8,8 @@ pub struct UserCommunityUtil; impl UserCommunityUtil { pub fn add_community(storage_owner: Uuid, address: String, title: String, position: String) { - let path = format!("users/{}/communities.json", storage_owner); - let mut communities: JsonValue = Self::load_array(&path); + let file_path = format!("users/{}/", storage_owner); + let mut communities = Self::load_array(&file_path); let mut community = JsonValue::new_object(); community["title"] = JsonValue::String(title); @@ -16,44 +17,52 @@ impl UserCommunityUtil { community["position"] = JsonValue::String(position); communities.push(community); - Self::save_array(&path, communities); + + save_file( + &file_path, + "communities.json", + &JsonValue::Array(communities).to_string(), + ); } pub fn remove_community(storage_owner: Uuid, community_address: String) { - let path = format!("users/{}/communities.json", storage_owner); - let mut communities = Self::load_array(&path); + let file_path = format!("users/{}/", storage_owner); + let communities = Self::load_array(&file_path); - let mut new_array = JsonValue::new_array(); - for entry in communities.members() { - if entry["address"].as_str() != Some(&community_address) { - new_array.push(entry.clone()).unwrap(); + let filtered: Array = communities + .iter() + .filter(|entry| entry["address"].as_str() != Some(&community_address)) + .cloned() + .collect(); + save_file( + &file_path, + "communities.json", + &JsonValue::Array(filtered).to_string(), + ); + } + + pub fn get_communities(storage_owner: Uuid) -> Array { + let file_path = format!("users/{}/communities.json", storage_owner); + Self::load_array(&file_path) + } + + fn load_array(file_path: &str) -> Array { + if !Path::new(file_path).exists() { + return Array::new(); + } + + match fs::read_to_string(file_path) { + Ok(content) => { + let parsed = json::parse(&content); + match parsed { + Ok(JsonValue::Array(arr)) => arr, + _ => Array::new(), + } + } + Err(err) => { + eprintln!("Failed to read file {}: {}", file_path, err); + Array::new() } } - - Self::save_array(&path, new_array); - } - - pub fn get_communities(storage_owner: Uuid) -> JsonValue { - let path = format!("users/{}/communities.json", storage_owner); - Self::load_array(&path) - } - - fn load_array(path: &str) -> JsonValue { - if !Path::new(path).exists() { - return JsonValue::new_object(); - } - - match fs::read_to_string(path) { - Ok(content) => json::parse(&content).unwrap_or_else(|_| JsonValue::new_object()), - Err(_) => JsonValue::new_object(), - } - } - - fn save_array(path: &str, arr: JsonValue) { - if let Some(parent) = Path::new(path).parent() { - let _ = fs::create_dir_all(parent); - } - - let _ = fs::write(path, arr.pretty(3)); } } diff --git a/src/users/user_profile.rs b/src/users/user_profile.rs index 88b8b06..2e2dd86 100644 --- a/src/users/user_profile.rs +++ b/src/users/user_profile.rs @@ -1,4 +1,5 @@ use crate::auth::auth_connector::AuthConnector; +use crate::gui::log_panel::log_message; use crate::users::user_manager::UserManager; use base64::{Engine as _, engine::general_purpose}; use json::{JsonValue, object, stringify}; @@ -72,15 +73,14 @@ impl UserProfile { reset_token, ); - // Migration hook (stubbed, since AuthConnector isn’t implemented here) if j.has_key("migrate") || j.has_key("migrating") || j.has_key("changing") || j.has_key("move") || j.has_key("moving") { - if AuthConnector::migrate_user(&mut up, stringify!("{}", Uuid::new_v4())).await { - println!("[INFO] Migration triggered for {}", up.username); + if AuthConnector::migrate_user(&mut up).await { + log_message(format!("[INFO] Migration triggered for {}", up.username)); UserManager::set_unique(true); } } @@ -93,7 +93,6 @@ impl UserProfile { OsRng.fill(bytes.as_mut()); let new_token = general_purpose::STANDARD.encode(&bytes); self.reset_token = new_token.clone(); - UserManager::save_users().ok(); new_token } diff --git a/src/util/chat_files.rs b/src/util/chat_files.rs index 1a67e8a..c8b965d 100644 --- a/src/util/chat_files.rs +++ b/src/util/chat_files.rs @@ -37,11 +37,11 @@ impl ChatFiles { } } - fn save_file(dir: &str, file_name: &str, content: &str) -> std::io::Result<()> { - fs::create_dir_all(dir)?; + fn save_file(dir: &str, file_name: &str, content: &str) { + fs::create_dir_all(dir); let path = Path::new(dir).join(file_name); - let mut file = File::create(path)?; - file.write_all(content.as_bytes()) + let mut file = File::create(path).unwrap(); + file.write_all(content.as_bytes()); } pub fn add_message( send_time: i64, @@ -49,10 +49,7 @@ impl ChatFiles { storage_owner: Uuid, external_user: Uuid, message: &str, - avatar: bool, - timestamp: bool, - tint: i64, - ) -> std::io::Result<()> { + ) { let user_dir = format!("users/{}/chats/{}", storage_owner, external_user); let mut chunk_index = 0; @@ -88,7 +85,7 @@ impl ChatFiles { &user_dir, &format!("msgs_{}.json", chunk_index), &message_chunk.dump(), - ) + ); } pub fn change_message_state( @@ -127,7 +124,7 @@ impl ChatFiles { } if modified { - Self::save_file(&user_dir, &fname_str, &chunk.dump())?; + Self::save_file(&user_dir, &fname_str, &chunk.dump()); break; } }