Connection With Omega
This commit is contained in:
parent
4d8c07a7e4
commit
504e5ff015
20 changed files with 880 additions and 320 deletions
74
Cargo.lock
generated
74
Cargo.lock
generated
|
|
@ -6,6 +6,7 @@ version = 4
|
||||||
name = "Omikron"
|
name = "Omikron"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aes-gcm",
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
"axum",
|
"axum",
|
||||||
|
|
@ -38,6 +39,7 @@ dependencies = [
|
||||||
"sys-info",
|
"sys-info",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-native-tls",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
|
@ -59,6 +61,16 @@ version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aead"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aes"
|
name = "aes"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
|
|
@ -70,6 +82,20 @@ dependencies = [
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes-gcm"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
|
||||||
|
dependencies = [
|
||||||
|
"aead",
|
||||||
|
"aes",
|
||||||
|
"cipher",
|
||||||
|
"ctr",
|
||||||
|
"ghash",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
|
|
@ -759,9 +785,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
|
"rand_core 0.6.4",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctr"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||||
|
dependencies = [
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx"
|
name = "cxx"
|
||||||
version = "1.0.189"
|
version = "1.0.189"
|
||||||
|
|
@ -1235,6 +1271,16 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ghash"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
|
||||||
|
dependencies = [
|
||||||
|
"opaque-debug",
|
||||||
|
"polyval",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gio"
|
name = "gio"
|
||||||
version = "0.21.4"
|
version = "0.21.4"
|
||||||
|
|
@ -2163,6 +2209,12 @@ version = "1.21.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.75"
|
version = "0.10.75"
|
||||||
|
|
@ -2385,6 +2437,18 @@ dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "polyval"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
"universal-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "potential_utf"
|
name = "potential_utf"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
|
@ -3741,6 +3805,16 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "universal-hash"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ansi_term = "*"
|
ansi_term = "*"
|
||||||
async-tungstenite = { version = "0.32.0", features = ["futures-03-sink", "futures-util", "handshake", "__rustls-tls", "async-native-tls", "async-std", "async-std-runtime", "async-tls", "gio", "gio-runtime", "glib", "openssl", "real-async-native-tls", "real-async-tls", "real-native-tls", "real-tokio-native-tls", "real-tokio-openssl", "real-tokio-rustls", "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-native-tls", "tokio-openssl", "tokio-runtime", "tokio-rustls-manual-roots", "tokio-rustls-native-certs", "tokio-rustls-webpki-roots", "url", "verbose-logging", "webpki-roots" ] }
|
async-tungstenite = { version = "0.32.0", features = ["futures-03-sink", "futures-util", "handshake", "__rustls-tls", "async-native-tls", "async-std", "async-std-runtime", "async-tls", "gio", "gio-runtime", "glib", "openssl", "real-async-native-tls", "real-async-tls", "real-native-tls", "real-tokio-native-tls", "real-tokio-openssl", "real-tokio-rustls", "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-native-tls", "tokio-openssl", "tokio-runtime", "tokio-rustls-manual-roots", "tokio-rustls-native-certs", "tokio-rustls-webpki-roots", "url", "verbose-logging", "webpki-roots"] }
|
||||||
axum = "*"
|
axum = "*"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
bytes = "*"
|
bytes = "*"
|
||||||
|
|
@ -49,3 +49,5 @@ log = "0.4"
|
||||||
livekit = "0.7.25"
|
livekit = "0.7.25"
|
||||||
livekit-api = "0.4.10"
|
livekit-api = "0.4.10"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
aes-gcm = "0.10.3"
|
||||||
|
tokio-native-tls = "0.3.1"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||||
|
use crate::log;
|
||||||
use crate::util::config_util::CONFIG;
|
use crate::util::config_util::CONFIG;
|
||||||
use crate::util::print::{PrintType, line};
|
use crate::util::logger::PrintType;
|
||||||
use json::number::Number;
|
use json::number::Number;
|
||||||
use reqwest::{Client, Response};
|
use reqwest::{Client, Response};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
@ -26,6 +27,19 @@ fn client() -> Client {
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
pub async fn get_auth_public_key() -> Option<String> {
|
||||||
|
let url = format!("https://auth.tensamin.net/api/get/public_key");
|
||||||
|
let client = client();
|
||||||
|
let res = client.get(&url).send().await.ok()?;
|
||||||
|
let json = res.text().await.ok()?;
|
||||||
|
|
||||||
|
let cv = CommunicationValue::from_json(&json);
|
||||||
|
if cv.comm_type != CommunicationType::success {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(cv.get_data(DataTypes::public_key).unwrap().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_user(user_id: Uuid) -> Option<AuthUser> {
|
pub async fn get_user(user_id: Uuid) -> Option<AuthUser> {
|
||||||
let url = format!("https://auth.tensamin.net/api/get/{}", user_id);
|
let url = format!("https://auth.tensamin.net/api/get/{}", user_id);
|
||||||
|
|
@ -83,7 +97,7 @@ pub async fn get_iota_id(user_id: i64) -> Option<i64> {
|
||||||
|
|
||||||
let cv = CommunicationValue::from_json(&json);
|
let cv = CommunicationValue::from_json(&json);
|
||||||
if cv.comm_type != CommunicationType::success {
|
if cv.comm_type != CommunicationType::success {
|
||||||
line(PrintType::IotaIn, &cv.to_json().to_string());
|
log!(PrintType::Iota, "{}", &cv.to_json().to_string());
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
129
src/auth/crypto_helper.rs
Normal file
129
src/auth/crypto_helper.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
use aes_gcm::{
|
||||||
|
Aes256Gcm, Nonce,
|
||||||
|
aead::{Aead, KeyInit, OsRng},
|
||||||
|
};
|
||||||
|
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||||
|
use rand_core::RngCore;
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
use x448::{PublicKey, Secret, SharedSecret};
|
||||||
|
|
||||||
|
/// Errors for crypto operations
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CryptoError {
|
||||||
|
Base64Decode(base64::DecodeError),
|
||||||
|
InvalidKey,
|
||||||
|
AgreementError,
|
||||||
|
EncryptionError(aes_gcm::Error),
|
||||||
|
DecryptionError(aes_gcm::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<base64::DecodeError> for CryptoError {
|
||||||
|
fn from(err: base64::DecodeError) -> Self {
|
||||||
|
CryptoError::Base64Decode(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KeyPair {
|
||||||
|
pub secret: Secret,
|
||||||
|
pub public: PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_keypair() -> KeyPair {
|
||||||
|
let mut buf = [0u8; 56];
|
||||||
|
let mut rng = OsRng;
|
||||||
|
rng.fill_bytes(&mut buf);
|
||||||
|
let secret = Secret::from_bytes(&buf).unwrap();
|
||||||
|
let public = PublicKey::from(&secret);
|
||||||
|
KeyPair { secret, public }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn public_key_to_base64(pubkey: &PublicKey) -> String {
|
||||||
|
STANDARD.encode(pubkey.as_bytes().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn secret_key_to_base64(secret: &Secret) -> String {
|
||||||
|
STANDARD.encode(secret.as_bytes().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_public_key(base64_pub: &str) -> Option<PublicKey> {
|
||||||
|
let bytes = STANDARD.decode(base64_pub).unwrap();
|
||||||
|
PublicKey::from_bytes(&bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_secret_key(base64_secret: &str) -> Option<Secret> {
|
||||||
|
let bytes = STANDARD.decode(base64_secret).unwrap();
|
||||||
|
Secret::from_bytes(&bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive_aes_key(shared: &SharedSecret) -> [u8; 32] {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(shared.as_bytes());
|
||||||
|
let result = hasher.finalize();
|
||||||
|
let mut key = [0u8; 32];
|
||||||
|
key.copy_from_slice(&result[..32]);
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encrypt(
|
||||||
|
base64_secret: &str,
|
||||||
|
base64_peer_pub: &str,
|
||||||
|
plaintext: &str,
|
||||||
|
) -> Result<String, CryptoError> {
|
||||||
|
let secret = load_secret_key(base64_secret).unwrap();
|
||||||
|
let peer_pub = load_public_key(base64_peer_pub).unwrap();
|
||||||
|
let shared = secret
|
||||||
|
.to_diffie_hellman(&peer_pub)
|
||||||
|
.ok_or(CryptoError::AgreementError)?;
|
||||||
|
let key_bytes = derive_aes_key(&shared);
|
||||||
|
let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct");
|
||||||
|
let mut nonce_bytes = [0u8; 12];
|
||||||
|
OsRng.fill_bytes(&mut nonce_bytes);
|
||||||
|
let nonce = Nonce::from_slice(&nonce_bytes);
|
||||||
|
let ciphertext = cipher
|
||||||
|
.encrypt(nonce, plaintext.as_bytes())
|
||||||
|
.map_err(CryptoError::EncryptionError)?;
|
||||||
|
// prefix nonce to ciphertext
|
||||||
|
let mut out = Vec::with_capacity(nonce_bytes.len() + ciphertext.len());
|
||||||
|
out.extend_from_slice(&nonce_bytes);
|
||||||
|
out.extend_from_slice(&ciphertext);
|
||||||
|
Ok(STANDARD.encode(&out))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrypt(
|
||||||
|
base64_secret: &str,
|
||||||
|
base64_peer_pub: &str,
|
||||||
|
encrypted_base64: &str,
|
||||||
|
) -> Result<String, CryptoError> {
|
||||||
|
let secret = load_secret_key(base64_secret).unwrap();
|
||||||
|
let peer_pub = load_public_key(base64_peer_pub).unwrap();
|
||||||
|
let shared = secret
|
||||||
|
.to_diffie_hellman(&peer_pub)
|
||||||
|
.ok_or(CryptoError::AgreementError)?;
|
||||||
|
let key_bytes = derive_aes_key(&shared);
|
||||||
|
let cipher = Aes256Gcm::new_from_slice(&key_bytes).expect("Key length should be correct");
|
||||||
|
|
||||||
|
let encrypted = STANDARD.decode(encrypted_base64)?;
|
||||||
|
if encrypted.len() < 12 {
|
||||||
|
return Err(CryptoError::DecryptionError(aes_gcm::Error));
|
||||||
|
}
|
||||||
|
let nonce_bytes = &encrypted[..12];
|
||||||
|
let ciphertext = &encrypted[12..];
|
||||||
|
let nonce = Nonce::from_slice(nonce_bytes);
|
||||||
|
let plaintext_bytes = cipher
|
||||||
|
.decrypt(nonce, ciphertext)
|
||||||
|
.map_err(CryptoError::DecryptionError)?;
|
||||||
|
let plaintext = String::from_utf8(plaintext_bytes)
|
||||||
|
.map_err(|_| CryptoError::DecryptionError(aes_gcm::Error))?;
|
||||||
|
Ok(plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash_it(input: &str) -> Vec<u8> {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(input.as_bytes());
|
||||||
|
hasher.finalize().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hex_hash(input: &str) -> String {
|
||||||
|
let digest = hash_it(input);
|
||||||
|
digest.iter().map(|b| format!("{:02x}", b)).collect()
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
pub mod auth_connector;
|
pub mod auth_connector;
|
||||||
|
pub mod crypto_helper;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,4 @@ impl CallGroup {
|
||||||
show: RwLock::new(true),
|
show: RwLock::new(true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_member(self: Arc<Self>, member: i64, inviter: i64) {
|
|
||||||
*self.show.write().await = true;
|
|
||||||
self.members
|
|
||||||
.write()
|
|
||||||
.await
|
|
||||||
.push(Arc::new(Caller::new(member, self.call_id, inviter)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,24 +6,12 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
calls::{call_group::CallGroup, caller::Caller},
|
calls::{call_group::CallGroup, caller::Caller},
|
||||||
util::print::{PrintType, line},
|
log,
|
||||||
|
util::logger::PrintType,
|
||||||
};
|
};
|
||||||
|
|
||||||
static CALL_GROUPS: Lazy<RwLock<Vec<Arc<CallGroup>>>> = Lazy::new(|| RwLock::new(Vec::new()));
|
static CALL_GROUPS: Lazy<RwLock<Vec<Arc<CallGroup>>>> = Lazy::new(|| RwLock::new(Vec::new()));
|
||||||
|
|
||||||
pub async fn get_call_invites(user_id: i64) -> Vec<Arc<Caller>> {
|
|
||||||
let mut callers = Vec::new();
|
|
||||||
for cg in CALL_GROUPS.read().await.iter() {
|
|
||||||
let members = cg.members.read().await;
|
|
||||||
for member in members.iter() {
|
|
||||||
if member.user_id == user_id {
|
|
||||||
callers.push(member.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callers
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_call_groups(user_id: i64) -> Vec<Arc<CallGroup>> {
|
pub async fn get_call_groups(user_id: i64) -> Vec<Arc<CallGroup>> {
|
||||||
let mut call_groups = Vec::new();
|
let mut call_groups = Vec::new();
|
||||||
for cg in CALL_GROUPS.read().await.iter() {
|
for cg in CALL_GROUPS.read().await.iter() {
|
||||||
|
|
@ -45,38 +33,28 @@ pub async fn get_call_token(user_id: i64, call_id: Uuid) -> Option<String> {
|
||||||
call_groups.iter().find(|g| g.call_id == call_id).cloned()
|
call_groups.iter().find(|g| g.call_id == call_id).cloned()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// if the group exists
|
||||||
if let Some(cg) = existing_group {
|
if let Some(cg) = existing_group {
|
||||||
let mut members = cg.members.write().await;
|
let members = cg.members.write().await;
|
||||||
|
|
||||||
|
// if the user is already a member
|
||||||
if let Some(member) = members.iter().find(|m| m.user_id == user_id) {
|
if let Some(member) = members.iter().find(|m| m.user_id == user_id) {
|
||||||
return Some(member.create_token());
|
return Some(member.create_token());
|
||||||
}
|
}
|
||||||
|
return None;
|
||||||
|
/*
|
||||||
let new_caller = Arc::new(Caller::new(user_id, call_id, user_id));
|
let new_caller = Arc::new(Caller::new(user_id, call_id, user_id));
|
||||||
let token = new_caller.create_token();
|
let token = new_caller.create_token();
|
||||||
|
|
||||||
members.push(new_caller);
|
members.push(new_caller);
|
||||||
|
|
||||||
return Some(token);
|
return Some(token);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut call_groups = CALL_GROUPS.write().await;
|
let mut call_groups = CALL_GROUPS.write().await;
|
||||||
|
|
||||||
if let Some(cg) = call_groups.iter().find(|g| g.call_id == call_id) {
|
let caller = Arc::new(Caller::new(user_id, call_id, true));
|
||||||
let cg_clone = cg.clone();
|
|
||||||
drop(call_groups);
|
|
||||||
|
|
||||||
let mut members = cg_clone.members.write().await;
|
|
||||||
if let Some(member) = members.iter().find(|m| m.user_id == user_id) {
|
|
||||||
return Some(member.create_token());
|
|
||||||
}
|
|
||||||
let new_caller = Arc::new(Caller::new(user_id, call_id, user_id));
|
|
||||||
let token = new_caller.create_token();
|
|
||||||
members.push(new_caller);
|
|
||||||
return Some(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
let caller = Arc::new(Caller::new(user_id, call_id, user_id));
|
|
||||||
let call_group = CallGroup::new(call_id, caller.clone());
|
let call_group = CallGroup::new(call_id, caller.clone());
|
||||||
|
|
||||||
call_groups.push(Arc::new(call_group));
|
call_groups.push(Arc::new(call_group));
|
||||||
|
|
@ -99,7 +77,7 @@ pub async fn add_invite(call_id: Uuid, inviter_id: i64, invitee_id: i64) -> bool
|
||||||
|
|
||||||
if is_inviter_member {
|
if is_inviter_member {
|
||||||
if !members.iter().any(|m| m.user_id == invitee_id) {
|
if !members.iter().any(|m| m.user_id == invitee_id) {
|
||||||
members.push(Arc::new(Caller::new(invitee_id, call_id, inviter_id)));
|
members.push(Arc::new(Caller::new(invitee_id, call_id, false)));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -138,13 +116,11 @@ pub async fn clean_calls() {
|
||||||
let size_post = call_groups.len();
|
let size_post = call_groups.len();
|
||||||
drop(call_groups);
|
drop(call_groups);
|
||||||
if size_pre - size_post != 0 {
|
if size_pre - size_post != 0 {
|
||||||
line(
|
log!(
|
||||||
PrintType::CallIn,
|
PrintType::Call,
|
||||||
&format!(
|
"Cleaned {} calls, {} remaining",
|
||||||
"Cleaned {} calls, {} remaining",
|
size_pre - size_post,
|
||||||
size_pre - size_post,
|
size_post
|
||||||
size_post
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ use livekit_api::access_token;
|
||||||
use std::env;
|
use std::env;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub fn create_token(user_id: i64, call_id: Uuid) -> Result<String, access_token::AccessTokenError> {
|
pub fn create_token(
|
||||||
|
user_id: i64,
|
||||||
|
call_id: Uuid,
|
||||||
|
has_admin: bool,
|
||||||
|
) -> Result<String, access_token::AccessTokenError> {
|
||||||
let api_key = env::var("LIVEKIT_API_KEY").expect("LIVEKIT_API_KEY is not set");
|
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");
|
let api_secret = env::var("LIVEKIT_API_SECRET").expect("LIVEKIT_API_SECRET is not set");
|
||||||
|
|
||||||
|
|
@ -11,9 +15,11 @@ pub fn create_token(user_id: i64, call_id: Uuid) -> Result<String, access_token:
|
||||||
.with_grants(access_token::VideoGrants {
|
.with_grants(access_token::VideoGrants {
|
||||||
can_update_own_metadata: true,
|
can_update_own_metadata: true,
|
||||||
room_join: true,
|
room_join: true,
|
||||||
|
room_admin: has_admin,
|
||||||
room: call_id.to_string(),
|
room: call_id.to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
|
.with_metadata(&format!("{{\"isAdmin\":{}}}", has_admin))
|
||||||
.to_jwt();
|
.to_jwt();
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,25 @@ use crate::calls::call_util;
|
||||||
pub struct Caller {
|
pub struct Caller {
|
||||||
pub user_id: i64,
|
pub user_id: i64,
|
||||||
pub call_id: Uuid,
|
pub call_id: Uuid,
|
||||||
pub inviters: Vec<i64>,
|
pub has_admin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Caller {
|
impl Caller {
|
||||||
pub fn new(user_id: i64, call_id: Uuid, inviter_id: i64) -> Self {
|
pub fn new(user_id: i64, call_id: Uuid, has_admin: bool) -> Self {
|
||||||
Caller {
|
Caller {
|
||||||
user_id,
|
user_id,
|
||||||
call_id,
|
call_id,
|
||||||
inviters: vec![inviter_id],
|
has_admin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn set_admin(&mut self, has_admin: bool) {
|
||||||
|
self.has_admin = has_admin;
|
||||||
|
}
|
||||||
|
pub fn has_admin(&self) -> bool {
|
||||||
|
self.has_admin
|
||||||
|
}
|
||||||
pub fn create_token(&self) -> String {
|
pub fn create_token(&self) -> String {
|
||||||
if let Ok(token) = call_util::create_token(self.user_id, self.call_id) {
|
if let Ok(token) = call_util::create_token(self.user_id, self.call_id, self.has_admin()) {
|
||||||
token
|
token
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ pub enum DataTypes {
|
||||||
shared_secret,
|
shared_secret,
|
||||||
call_id,
|
call_id,
|
||||||
call_token,
|
call_token,
|
||||||
|
untill,
|
||||||
|
enable,
|
||||||
start_date,
|
start_date,
|
||||||
end_date,
|
end_date,
|
||||||
receiver_id,
|
receiver_id,
|
||||||
|
|
@ -110,6 +112,8 @@ impl DataTypes {
|
||||||
"sharedsecret" => DataTypes::shared_secret,
|
"sharedsecret" => DataTypes::shared_secret,
|
||||||
"callid" => DataTypes::call_id,
|
"callid" => DataTypes::call_id,
|
||||||
"calltoken" => DataTypes::call_token,
|
"calltoken" => DataTypes::call_token,
|
||||||
|
"untill" => DataTypes::untill,
|
||||||
|
"enable" => DataTypes::enable,
|
||||||
"startdate" => DataTypes::start_date,
|
"startdate" => DataTypes::start_date,
|
||||||
"enddate" => DataTypes::end_date,
|
"enddate" => DataTypes::end_date,
|
||||||
"receiverid" => DataTypes::receiver_id,
|
"receiverid" => DataTypes::receiver_id,
|
||||||
|
|
@ -159,11 +163,14 @@ impl DataTypes {
|
||||||
pub enum CommunicationType {
|
pub enum CommunicationType {
|
||||||
error,
|
error,
|
||||||
error_invalid_user_id,
|
error_invalid_user_id,
|
||||||
|
error_invalid_omikron_id,
|
||||||
error_not_found,
|
error_not_found,
|
||||||
|
error_not_authenticated,
|
||||||
error_no_iota,
|
error_no_iota,
|
||||||
error_invalid_challenge,
|
error_invalid_challenge,
|
||||||
error_invalid_secret,
|
error_invalid_secret,
|
||||||
error_invalid_private_key,
|
error_invalid_private_key,
|
||||||
|
error_invalid_public_key,
|
||||||
error_no_user_id,
|
error_no_user_id,
|
||||||
error_no_call_id,
|
error_no_call_id,
|
||||||
error_invalid_call_id,
|
error_invalid_call_id,
|
||||||
|
|
@ -210,6 +217,9 @@ pub enum CommunicationType {
|
||||||
watch_stream,
|
watch_stream,
|
||||||
call_token,
|
call_token,
|
||||||
call_invite,
|
call_invite,
|
||||||
|
call_disconnect_user,
|
||||||
|
call_timeout_user,
|
||||||
|
call_set_anonymous_joining,
|
||||||
end_call,
|
end_call,
|
||||||
function,
|
function,
|
||||||
update,
|
update,
|
||||||
|
|
@ -223,14 +233,20 @@ impl CommunicationType {
|
||||||
"watchstream" => CommunicationType::watch_stream,
|
"watchstream" => CommunicationType::watch_stream,
|
||||||
"calltoken" => CommunicationType::call_token,
|
"calltoken" => CommunicationType::call_token,
|
||||||
"callinvite" => CommunicationType::call_invite,
|
"callinvite" => CommunicationType::call_invite,
|
||||||
|
"calldisconnectuser" => CommunicationType::call_disconnect_user,
|
||||||
|
"calltimeoutuser" => CommunicationType::call_timeout_user,
|
||||||
|
"callsetanonymousjoining" => CommunicationType::call_set_anonymous_joining,
|
||||||
"endcall" => CommunicationType::end_call,
|
"endcall" => CommunicationType::end_call,
|
||||||
"function" => CommunicationType::function,
|
"function" => CommunicationType::function,
|
||||||
"update" => CommunicationType::update,
|
"update" => CommunicationType::update,
|
||||||
"createuser" => CommunicationType::create_user,
|
"createuser" => CommunicationType::create_user,
|
||||||
"errorinvaliduserid" => CommunicationType::error_invalid_user_id,
|
"errorinvaliduserid" => CommunicationType::error_invalid_user_id,
|
||||||
|
"errorinvalidomikronid" => CommunicationType::error_invalid_omikron_id,
|
||||||
"errornotfound" => CommunicationType::error_not_found,
|
"errornotfound" => CommunicationType::error_not_found,
|
||||||
|
"errornotauthenticated" => CommunicationType::error_not_authenticated,
|
||||||
"errornoiota" => CommunicationType::error_no_iota,
|
"errornoiota" => CommunicationType::error_no_iota,
|
||||||
"errorinvalidchallenge" => CommunicationType::error_invalid_challenge,
|
"errorinvalidchallenge" => CommunicationType::error_invalid_challenge,
|
||||||
|
"errorinvalidpublickey" => CommunicationType::error_invalid_public_key,
|
||||||
"errorinvalidsecret" => CommunicationType::error_invalid_secret,
|
"errorinvalidsecret" => CommunicationType::error_invalid_secret,
|
||||||
"errorinvalidprivatekey" => CommunicationType::error_invalid_private_key,
|
"errorinvalidprivatekey" => CommunicationType::error_invalid_private_key,
|
||||||
"errornouserid" => CommunicationType::error_no_user_id,
|
"errornouserid" => CommunicationType::error_no_user_id,
|
||||||
|
|
@ -243,7 +259,7 @@ impl CommunicationType {
|
||||||
"message" => CommunicationType::message,
|
"message" => CommunicationType::message,
|
||||||
"messagesend" => CommunicationType::message_send,
|
"messagesend" => CommunicationType::message_send,
|
||||||
"messagelive" => CommunicationType::message_live,
|
"messagelive" => CommunicationType::message_live,
|
||||||
"messageother_iota" => CommunicationType::message_other_iota,
|
"messageotheriota" => CommunicationType::message_other_iota,
|
||||||
"messagechunk" => CommunicationType::message_chunk,
|
"messagechunk" => CommunicationType::message_chunk,
|
||||||
"messagesget" => CommunicationType::messages_get,
|
"messagesget" => CommunicationType::messages_get,
|
||||||
"changeconfirm" => CommunicationType::change_confirm,
|
"changeconfirm" => CommunicationType::change_confirm,
|
||||||
|
|
|
||||||
44
src/main.rs
44
src/main.rs
|
|
@ -8,33 +8,46 @@ mod util;
|
||||||
use async_tungstenite::accept_hdr_async;
|
use async_tungstenite::accept_hdr_async;
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use std::sync::Arc;
|
use once_cell::sync::Lazy;
|
||||||
|
use std::{env, sync::Arc};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio_util::compat::TokioAsyncReadCompatExt;
|
use tokio_util::compat::TokioAsyncReadCompatExt;
|
||||||
use tungstenite::handshake::server::{Request, Response};
|
use tungstenite::handshake::server::{Request, Response};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
auth::crypto_helper::{load_public_key, load_secret_key},
|
||||||
calls::call_manager::garbage_collect_calls,
|
calls::call_manager::garbage_collect_calls,
|
||||||
omega::omega_connection::OmegaConnection,
|
omega::omega_connection::OmegaConnection,
|
||||||
rho::{client_connection::ClientConnection, iota_connection::IotaConnection},
|
rho::{client_connection::ClientConnection, iota_connection::IotaConnection},
|
||||||
util::{
|
util::{
|
||||||
config_util::CONFIG,
|
config_util::CONFIG,
|
||||||
print::{PrintType, line, line_err},
|
logger::{PrintType, startup},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PRIVATE_KEY: Lazy<String> = Lazy::new(|| env::var("PRIVATE_KEY").unwrap());
|
||||||
|
pub fn get_private_key() -> x448::Secret {
|
||||||
|
load_secret_key(&*PRIVATE_KEY).unwrap()
|
||||||
|
}
|
||||||
|
static PUBLIC_KEY: Lazy<String> = Lazy::new(|| env::var("PUBLIC_KEY").unwrap());
|
||||||
|
pub fn get_public_key() -> x448::PublicKey {
|
||||||
|
load_public_key(&*PUBLIC_KEY).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
OmegaConnection::new().connect().await;
|
Arc::new(OmegaConnection::new()).connect();
|
||||||
});
|
});
|
||||||
|
startup();
|
||||||
let address = format!("{}:{}", &CONFIG.read().await.ip, &CONFIG.read().await.port);
|
let address = format!("{}:{}", &CONFIG.read().await.ip, &CONFIG.read().await.port);
|
||||||
let listener = TcpListener::bind(&address).await.unwrap();
|
let listener = TcpListener::bind(&address).await.unwrap();
|
||||||
|
|
||||||
line(
|
log!(
|
||||||
PrintType::General,
|
PrintType::General,
|
||||||
&format!("WebSocket server listening on {}", &address),
|
"WebSocket server listening on {}",
|
||||||
|
address,
|
||||||
);
|
);
|
||||||
|
|
||||||
garbage_collect_calls();
|
garbage_collect_calls();
|
||||||
|
|
@ -50,16 +63,13 @@ async fn main() {
|
||||||
let ws_stream = match accept_hdr_async(stream.compat(), callback).await {
|
let ws_stream = match accept_hdr_async(stream.compat(), callback).await {
|
||||||
Ok(ws) => ws,
|
Ok(ws) => ws,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
line_err(
|
log!(PrintType::General, "WebSocket upgrade failed: {}", e,);
|
||||||
PrintType::General,
|
|
||||||
&format!("WebSocket upgrade failed: {}", e),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (sender, receiver) = ws_stream.split();
|
let (sender, receiver) = ws_stream.split();
|
||||||
if path == "/ws/client/" {
|
if path == "/ws/client/" {
|
||||||
line(PrintType::ClientIn, "New Client connection");
|
log_in!(PrintType::Client, "New Client connection");
|
||||||
let client_conn: Arc<ClientConnection> =
|
let client_conn: Arc<ClientConnection> =
|
||||||
Arc::from(ClientConnection::new(sender, receiver));
|
Arc::from(ClientConnection::new(sender, receiver));
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -74,25 +84,25 @@ async fn main() {
|
||||||
let text = msg.into_text().unwrap();
|
let text = msg.into_text().unwrap();
|
||||||
client_conn.clone().handle_message(text).await;
|
client_conn.clone().handle_message(text).await;
|
||||||
} else if msg.is_close() {
|
} else if msg.is_close() {
|
||||||
line(PrintType::ClientIn, "Client disconnected");
|
log_in!(PrintType::Client, "Client disconnected");
|
||||||
client_conn.handle_close().await;
|
client_conn.handle_close().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
line_err(PrintType::ClientIn, &format!("WebSocket error: {}", e));
|
log_err!(PrintType::Client, "WebSocket error: {}", e);
|
||||||
client_conn.handle_close().await;
|
client_conn.handle_close().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
line(PrintType::ClientIn, "Client stream ended");
|
log_in!(PrintType::Client, "Client stream ended");
|
||||||
client_conn.handle_close().await;
|
client_conn.handle_close().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if path == "/ws/iota/" {
|
} else if path == "/ws/iota/" {
|
||||||
line(PrintType::IotaIn, "New Iota connection");
|
log_in!(PrintType::Iota, "New Iota connection");
|
||||||
let iota_conn: Arc<IotaConnection> =
|
let iota_conn: Arc<IotaConnection> =
|
||||||
Arc::from(IotaConnection::new(sender, receiver));
|
Arc::from(IotaConnection::new(sender, receiver));
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -107,19 +117,19 @@ async fn main() {
|
||||||
let text = msg.into_text().unwrap();
|
let text = msg.into_text().unwrap();
|
||||||
iota_conn.clone().handle_message(text).await;
|
iota_conn.clone().handle_message(text).await;
|
||||||
} else if msg.is_close() {
|
} else if msg.is_close() {
|
||||||
line(PrintType::IotaIn, "Iota disconnected");
|
log_in!(PrintType::Iota, "Iota disconnected");
|
||||||
iota_conn.handle_close().await;
|
iota_conn.handle_close().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
line_err(PrintType::IotaIn, &format!("WebSocket error: {}", e));
|
log_err!(PrintType::Iota, "WebSocket error: {}", e);
|
||||||
iota_conn.handle_close().await;
|
iota_conn.handle_close().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Stream ended
|
// Stream ended
|
||||||
line(PrintType::IotaIn, "Iota stream ended");
|
log_in!(PrintType::Iota, "Iota stream ended");
|
||||||
iota_conn.handle_close().await;
|
iota_conn.handle_close().await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
pub mod omega_connection;
|
pub mod omega_connection;
|
||||||
|
pub mod ping_pong_task;
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,251 @@
|
||||||
use std::sync::Arc;
|
use async_tungstenite::{
|
||||||
use std::time::Duration;
|
WebSocketReceiver, WebSocketSender, WebSocketStream,
|
||||||
|
stream::Stream,
|
||||||
use crate::data::communication::{CommunicationType, CommunicationValue};
|
tokio::{TokioAdapter, connect_async},
|
||||||
use crate::util::print::PrintType;
|
tungstenite::protocol::Message,
|
||||||
use crate::util::print::{line, line_err};
|
|
||||||
use crate::{
|
|
||||||
data::{
|
|
||||||
communication::DataTypes,
|
|
||||||
user::{User, UserStatus},
|
|
||||||
},
|
|
||||||
rho::rho_manager,
|
|
||||||
util::config_util::CONFIG,
|
|
||||||
};
|
};
|
||||||
use async_tungstenite::tungstenite::protocol::Message;
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use futures::StreamExt;
|
use futures::prelude::*;
|
||||||
use json::JsonValue;
|
use json::{JsonValue, number::Number};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use tokio::sync::Mutex;
|
use std::{collections::HashMap, env, sync::Arc, time::Duration};
|
||||||
use tokio::time::sleep;
|
use tokio::{
|
||||||
use tokio_util::compat::Compat;
|
net::TcpStream,
|
||||||
use tungstenite::{Utf8Bytes, connect};
|
sync::{Mutex, RwLock},
|
||||||
|
time::{Instant, sleep},
|
||||||
|
};
|
||||||
|
use tokio_native_tls::TlsStream;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
static WAITING_TASKS: Lazy<DashMap<Uuid, Box<dyn Fn(CommunicationValue) -> bool + Send + Sync>>> =
|
use crate::{
|
||||||
Lazy::new(DashMap::new);
|
auth::crypto_helper::decrypt,
|
||||||
|
data::{
|
||||||
|
communication::{CommunicationType, CommunicationValue, DataTypes},
|
||||||
|
user::UserStatus,
|
||||||
|
},
|
||||||
|
get_private_key, log_in, log_out,
|
||||||
|
rho::rho_manager,
|
||||||
|
util::logger::PrintType,
|
||||||
|
};
|
||||||
|
use crate::{auth::crypto_helper::secret_key_to_base64, log_err};
|
||||||
|
|
||||||
|
static WAITING_TASKS: Lazy<
|
||||||
|
DashMap<Uuid, Box<dyn Fn(Arc<OmegaConnection>, CommunicationValue) -> bool + Send + Sync>>,
|
||||||
|
> = Lazy::new(DashMap::new);
|
||||||
|
|
||||||
|
static GENERIC_TASK: Lazy<
|
||||||
|
Mutex<Option<Box<dyn Fn(Arc<OmegaConnection>, CommunicationValue) -> bool + Send + Sync>>>,
|
||||||
|
> = Lazy::new(|| Mutex::new(None));
|
||||||
|
|
||||||
|
static OMEGA_CONNECTION: Lazy<Arc<OmegaConnection>> = Lazy::new(|| {
|
||||||
|
let conn = Arc::new(OmegaConnection::new());
|
||||||
|
let conn_clone = conn.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
conn_clone.connect_internal(0).await;
|
||||||
|
});
|
||||||
|
conn
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn get_omega_connection() -> Arc<OmegaConnection> {
|
||||||
|
OMEGA_CONNECTION.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct OmegaConnection {
|
pub struct OmegaConnection {
|
||||||
ws_stream:
|
write: Arc<
|
||||||
Arc<Mutex<Option<async_tungstenite::WebSocketStream<Compat<tokio::net::TcpStream>>>>>,
|
RwLock<
|
||||||
|
Option<
|
||||||
|
WebSocketSender<
|
||||||
|
Stream<TokioAdapter<TcpStream>, TokioAdapter<TlsStream<TcpStream>>>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
read: Arc<
|
||||||
|
RwLock<
|
||||||
|
Option<
|
||||||
|
WebSocketReceiver<
|
||||||
|
Stream<TokioAdapter<TcpStream>, TokioAdapter<TlsStream<TcpStream>>>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
pingpong: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>,
|
||||||
|
pub last_ping: Arc<Mutex<i64>>,
|
||||||
|
pub message_send_times: Arc<Mutex<HashMap<Uuid, Instant>>>,
|
||||||
|
pub is_connected: Arc<RwLock<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OmegaConnection {
|
impl OmegaConnection {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
OmegaConnection {
|
OmegaConnection {
|
||||||
ws_stream: Arc::new(Mutex::new(None)),
|
read: Arc::new(RwLock::new(None)),
|
||||||
|
write: Arc::new(RwLock::new(None)),
|
||||||
|
pingpong: Arc::new(Mutex::new(None)),
|
||||||
|
last_ping: Arc::new(Mutex::new(-1)),
|
||||||
|
message_send_times: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
is_connected: Arc::new(RwLock::new(false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn connect(self: Arc<OmegaConnection>) {
|
||||||
pub async fn connect(&self) {
|
let cloned_self = self.clone();
|
||||||
self.connect_internal(0).await;
|
tokio::spawn(async move {
|
||||||
|
cloned_self.connect_internal(0).await;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
async fn connect_internal(&self, mut retry: usize) {
|
async fn connect_internal(self: Arc<OmegaConnection>, mut retry: usize) {
|
||||||
loop {
|
loop {
|
||||||
if retry > 5 {
|
if retry > 5 {
|
||||||
line_err(
|
log_err!(PrintType::Omega, "Max retry attempts reached, giving up.");
|
||||||
PrintType::OmegaIn,
|
|
||||||
&"Max retry attempts reached, giving up.",
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match connect("wss://tensamin.methanium.net/ws/omega") {
|
let url_str =
|
||||||
Ok((_, _)) => {
|
env::var("OMEGA_HOST").unwrap_or("wss://omega.tensamin.net/ws/omikron".to_string());
|
||||||
|
match connect_async(&url_str).await {
|
||||||
|
Ok((ws_stream, _)) => {
|
||||||
|
*self.is_connected.write().await = true;
|
||||||
|
log_in!(PrintType::Omega, "WebSocket connected to {}", url_str);
|
||||||
retry = 0;
|
retry = 0;
|
||||||
let identify_msg = CommunicationValue::new(CommunicationType::identification)
|
let (write, read) = ws_stream.split();
|
||||||
.add_data(
|
*self.read.write().await = Some(read);
|
||||||
DataTypes::uuid,
|
*self.write.write().await = Some(write);
|
||||||
JsonValue::String(CONFIG.read().await.omikron_id.to_string()),
|
|
||||||
);
|
|
||||||
self.send_message(&identify_msg).await;
|
|
||||||
|
|
||||||
let ws_stream_clone = self.ws_stream.clone();
|
let cloned_self = self.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
OmegaConnection::read_loop(ws_stream_clone).await;
|
cloned_self.clone().read_loop().await;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let cloned_self = self.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let id = Uuid::new_v4();
|
||||||
|
let identify_msg =
|
||||||
|
CommunicationValue::new(CommunicationType::identification)
|
||||||
|
.with_id(id)
|
||||||
|
.add_data(
|
||||||
|
DataTypes::omikron,
|
||||||
|
JsonValue::Number(Number::from(
|
||||||
|
env::var("ID")
|
||||||
|
.unwrap_or("0".to_string())
|
||||||
|
.parse::<i64>()
|
||||||
|
.unwrap_or(0),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
WAITING_TASKS.insert(
|
||||||
|
id,
|
||||||
|
Box::new(|selfc, cv| {
|
||||||
|
if cv.is_type(CommunicationType::error_not_found) {
|
||||||
|
log_err!(
|
||||||
|
PrintType::Omega,
|
||||||
|
"Identification failed: Omikron ID not found on Omega.",
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !cv.is_type(CommunicationType::challenge) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let task = async move {
|
||||||
|
let challenge = cv
|
||||||
|
.get_data(DataTypes::challenge)
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
"Challenge not found or not a string".to_string()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let server_pub_key = cv
|
||||||
|
.get_data(DataTypes::public_key)
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
"Public key from server not found or not a string"
|
||||||
|
.to_string()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let decrypted_challenge = decrypt(
|
||||||
|
&secret_key_to_base64(&get_private_key()),
|
||||||
|
server_pub_key,
|
||||||
|
challenge,
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
format!("Failed to decrypt challenge: {:?}", e)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let response_msg = CommunicationValue::new(
|
||||||
|
CommunicationType::challenge_response,
|
||||||
|
)
|
||||||
|
.with_id(cv.get_id())
|
||||||
|
.add_data(
|
||||||
|
DataTypes::challenge,
|
||||||
|
JsonValue::String(decrypted_challenge),
|
||||||
|
);
|
||||||
|
|
||||||
|
let response_id = response_msg.get_id();
|
||||||
|
WAITING_TASKS.insert(
|
||||||
|
response_id,
|
||||||
|
Box::new(|_self, final_cv| {
|
||||||
|
if !final_cv
|
||||||
|
.is_type(CommunicationType::identification_response)
|
||||||
|
{
|
||||||
|
log_err!(
|
||||||
|
PrintType::Omega,
|
||||||
|
"Expected identification_response, got something else.",
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_err!(
|
||||||
|
PrintType::Omega,
|
||||||
|
"Successfully identified with Omega.",
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
selfc.send_message(&response_msg).await;
|
||||||
|
|
||||||
|
Ok::<(), String>(())
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = task.await {
|
||||||
|
log_err!(PrintType::Omega, "{}", &e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
true
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
cloned_self.send_message(&identify_msg).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let cloned_self = self.clone();
|
||||||
|
let handle = tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
if *cloned_self.is_connected.read().await == false {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cloned_self.send_ping().await;
|
||||||
|
sleep(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
*self.is_connected.write().await = true;
|
||||||
|
*self.pingpong.lock().await = Some(handle);
|
||||||
|
|
||||||
|
while *self.is_connected.read().await {
|
||||||
|
sleep(Duration::from_secs(2)).await;
|
||||||
|
}
|
||||||
|
*self.read.write().await = None;
|
||||||
|
*self.write.write().await = None;
|
||||||
|
log_err!(PrintType::Omega, "Connection lost. Retrying...");
|
||||||
|
retry += 1;
|
||||||
|
sleep(Duration::from_secs(2)).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
line_err(
|
log_err!(
|
||||||
PrintType::OmegaIn,
|
PrintType::Omega,
|
||||||
&format!("WebSocket connection failed (attempt {}): {}", retry, e),
|
"WebSocket connection failed (attempt {}): {}",
|
||||||
|
retry + 1,
|
||||||
|
e,
|
||||||
);
|
);
|
||||||
retry += 1;
|
retry += 1;
|
||||||
sleep(Duration::from_secs(2)).await;
|
sleep(Duration::from_secs(2)).await;
|
||||||
|
|
@ -79,72 +255,60 @@ impl OmegaConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_loop(
|
async fn read_loop(self: Arc<Self>) {
|
||||||
ws_stream: Arc<
|
|
||||||
Mutex<Option<async_tungstenite::WebSocketStream<Compat<tokio::net::TcpStream>>>>,
|
|
||||||
>,
|
|
||||||
) {
|
|
||||||
loop {
|
loop {
|
||||||
let mut lock = ws_stream.lock().await;
|
let msg = {
|
||||||
let Some(ws) = lock.as_mut() else {
|
let mut guard = self.read.write().await;
|
||||||
break;
|
let ws = match guard.as_mut() {
|
||||||
|
Some(ws) => ws,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
ws.next().await
|
||||||
};
|
};
|
||||||
|
|
||||||
match ws.next().await {
|
match msg {
|
||||||
Some(Ok(Message::Text(msg))) => {
|
Some(Ok(Message::Text(msg))) => {
|
||||||
let cv = CommunicationValue::from_json(&msg);
|
let cv = CommunicationValue::from_json(&msg);
|
||||||
|
if cv.is_type(CommunicationType::pong) {
|
||||||
|
self.handle_pong(&cv, true).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let msg_id = cv.get_id();
|
let msg_id = cv.get_id();
|
||||||
|
log_in!(PrintType::Omikron, "{}", &cv.to_json().to_string());
|
||||||
// Handle waiting tasks
|
// Handle waiting tasks
|
||||||
if let Some(task) = WAITING_TASKS.remove(&msg_id) {
|
if let Some(task) = WAITING_TASKS.remove(&msg_id) {
|
||||||
if (task.1)(cv.clone()) {
|
if (task.1)(self.clone(), cv.clone()) {
|
||||||
continue;
|
// continue in the read_loop
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
// Handle generic task
|
||||||
// Handle CLIENT_CHANGED
|
let generic_task_option = GENERIC_TASK.lock().await;
|
||||||
if cv.is_type(CommunicationType::client_changed) {
|
if let Some(generic_task) = generic_task_option.as_ref() {
|
||||||
let iota_id = cv
|
if generic_task(self.clone(), cv.clone()) {
|
||||||
.get_data(DataTypes::iota_id)
|
// continue in the read_loop
|
||||||
.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()
|
|
||||||
.as_str()
|
|
||||||
.unwrap();
|
|
||||||
let status = UserStatus::from_string(&status_str)
|
|
||||||
.unwrap_or(UserStatus::iota_offline);
|
|
||||||
|
|
||||||
let user = User::new(iota_id, user_id, status);
|
|
||||||
for rho_con in rho_manager::get_all_connections().await {
|
|
||||||
rho_con.are_they_interested(&user).await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Ok(Message::Close(_))) | None => {
|
Some(Ok(Message::Close(_))) | None => break,
|
||||||
break;
|
Some(Err(_)) => break,
|
||||||
}
|
|
||||||
Some(Err(_)) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*self.is_connected.write().await = false;
|
||||||
|
*self.read.write().await = None;
|
||||||
|
*self.write.write().await = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_message(&self, cv: &CommunicationValue) {
|
pub async fn send_message(&self, cv: &CommunicationValue) {
|
||||||
let mut guard = self.ws_stream.lock().await;
|
let mut guard = self.write.write().await;
|
||||||
if let Some(ws) = guard.as_mut() {
|
if let Some(ws) = guard.as_mut() {
|
||||||
line(PrintType::OmegaOut, &cv.to_json().to_string());
|
if !cv.is_type(CommunicationType::ping) {
|
||||||
|
log_out!(PrintType::Omega, "{}", &cv.to_json().to_string());
|
||||||
|
}
|
||||||
let _ = ws
|
let _ = ws
|
||||||
.send(Message::Text(Utf8Bytes::from(cv.to_json().to_string())))
|
.send(Message::Text(cv.to_json().to_string().into()))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -187,25 +351,26 @@ impl OmegaConnection {
|
||||||
|
|
||||||
WAITING_TASKS.insert(
|
WAITING_TASKS.insert(
|
||||||
msg_id,
|
msg_id,
|
||||||
Box::new(move |response: CommunicationValue| {
|
Box::new(
|
||||||
let _ = Box::pin(async move |_: CommunicationValue| {
|
move |_: Arc<OmegaConnection>, response: CommunicationValue| {
|
||||||
let rho = rho_manager::get_rho_con_for_user(user_id).await;
|
let _ = Box::pin(async move |_: CommunicationValue| {
|
||||||
if let Some(rho) = rho {
|
let rho = rho_manager::get_rho_con_for_user(user_id).await;
|
||||||
for client in rho.get_client_connections_for_user(user_id).await {
|
if let Some(rho) = rho {
|
||||||
client.send_message(&response).await;
|
for client in rho.get_client_connections_for_user(user_id).await {
|
||||||
|
client.send_message(&response).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
true
|
||||||
|
});
|
||||||
true
|
true
|
||||||
});
|
},
|
||||||
true
|
),
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
OmegaConnection::send_global(cv).await;
|
OmegaConnection::send_global(cv).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_global(cv: CommunicationValue) {
|
async fn send_global(cv: CommunicationValue) {
|
||||||
let conn = OmegaConnection::new();
|
OMEGA_CONNECTION.send_message(&cv).await;
|
||||||
conn.send_message(&cv).await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
42
src/omega/ping_pong_task.rs
Normal file
42
src/omega/ping_pong_task.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||||
|
use crate::omega::omega_connection::OmegaConnection;
|
||||||
|
use json::number::Number;
|
||||||
|
use tokio::time::Instant;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
impl OmegaConnection {
|
||||||
|
pub async fn send_ping(&self) {
|
||||||
|
let uuid = Uuid::new_v4();
|
||||||
|
let send_time = Instant::now();
|
||||||
|
|
||||||
|
self.message_send_times.lock().await.insert(uuid, send_time);
|
||||||
|
self.send_ping_message(uuid).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_ping_message(&self, uuid: Uuid) {
|
||||||
|
let ping_message = CommunicationValue::new(CommunicationType::ping)
|
||||||
|
.with_id(uuid)
|
||||||
|
.add_data_num(
|
||||||
|
DataTypes::last_ping,
|
||||||
|
Number::from(*self.last_ping.lock().await),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.send_message(&ping_message).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles incoming pong and calculates latency
|
||||||
|
pub async fn handle_pong(&self, cv: &CommunicationValue, _log: bool) {
|
||||||
|
let id = cv.get_id();
|
||||||
|
let send_time_opt = {
|
||||||
|
let queue = self.message_send_times.lock().await;
|
||||||
|
queue.get(&id).cloned()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(send_time) = send_time_opt {
|
||||||
|
let ping = Instant::now().duration_since(send_time).as_millis() as i64;
|
||||||
|
self.message_send_times.lock().await.remove(&id);
|
||||||
|
|
||||||
|
*self.last_ping.lock().await = ping as i64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,9 +9,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use super::{rho_connection::RhoConnection, rho_manager};
|
use super::{rho_connection::RhoConnection, rho_manager};
|
||||||
use crate::calls::call_manager;
|
use crate::calls::call_manager;
|
||||||
use crate::util::print::PrintType;
|
use crate::util::logger::PrintType;
|
||||||
use crate::util::print::line;
|
|
||||||
use crate::util::print::line_err;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
auth::auth_connector,
|
auth::auth_connector,
|
||||||
// calls::call_manager::CallManager,
|
// calls::call_manager::CallManager,
|
||||||
|
|
@ -21,6 +19,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
omega::omega_connection::OmegaConnection,
|
omega::omega_connection::OmegaConnection,
|
||||||
};
|
};
|
||||||
|
use crate::{log_in, log_out};
|
||||||
|
|
||||||
/// ClientConnection represents a WebSocket connection from a client device
|
/// ClientConnection represents a WebSocket connection from a client device
|
||||||
pub struct ClientConnection {
|
pub struct ClientConnection {
|
||||||
|
|
@ -94,17 +93,14 @@ impl ClientConnection {
|
||||||
.send(Message::Text(Utf8Bytes::from(message.to_string())))
|
.send(Message::Text(Utf8Bytes::from(message.to_string())))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
line_err(
|
log_out!(PrintType::Client, "Failed to send message to client: {}", e,);
|
||||||
PrintType::ClientOut,
|
|
||||||
&format!("Failed to send message to client: {}", e),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a CommunicationValue to the client
|
/// Send a CommunicationValue to the client
|
||||||
pub async fn send_message(&self, cv: &CommunicationValue) {
|
pub async fn send_message(&self, cv: &CommunicationValue) {
|
||||||
if !cv.is_type(CommunicationType::pong) {
|
if !cv.is_type(CommunicationType::pong) {
|
||||||
line(PrintType::ClientOut, &cv.to_json().to_string());
|
log_out!(PrintType::Client, "{}", &cv.to_json().to_string());
|
||||||
}
|
}
|
||||||
self.send_message_str(&cv.to_json().to_string()).await;
|
self.send_message_str(&cv.to_json().to_string()).await;
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +125,7 @@ impl ClientConnection {
|
||||||
self.handle_ping(cv).await;
|
self.handle_ping(cv).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
line(PrintType::ClientIn, &cv.to_json().to_string());
|
log_in!(PrintType::Client, "{}", &cv.to_json().to_string());
|
||||||
// Handle client status changes
|
// Handle client status changes
|
||||||
if cv.is_type(CommunicationType::client_changed) {
|
if cv.is_type(CommunicationType::client_changed) {
|
||||||
self.handle_client_changed(cv).await;
|
self.handle_client_changed(cv).await;
|
||||||
|
|
@ -181,7 +177,7 @@ impl ClientConnection {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
line(PrintType::ClientIn, "Missing private key");
|
log_in!(PrintType::Client, "Missing private key");
|
||||||
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_private_key)
|
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_private_key)
|
||||||
.await;
|
.await;
|
||||||
return;
|
return;
|
||||||
|
|
@ -246,8 +242,7 @@ impl ClientConnection {
|
||||||
async fn handle_client_changed(&self, cv: CommunicationValue) {
|
async fn handle_client_changed(&self, cv: CommunicationValue) {
|
||||||
let user_id = self.get_user_id().await;
|
let user_id = self.get_user_id().await;
|
||||||
if let Some(_status_str) = cv.get_data(DataTypes::user_state) {
|
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;
|
||||||
let user_status = UserStatus::online; // placeholder
|
|
||||||
if let Some(rho_conn) = self.get_rho_connection().await {
|
if let Some(rho_conn) = self.get_rho_connection().await {
|
||||||
OmegaConnection::client_changed(rho_conn.get_iota_id().await, user_id, user_status)
|
OmegaConnection::client_changed(rho_conn.get_iota_id().await, user_id, user_status)
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -354,6 +349,19 @@ impl ClientConnection {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async fn handle_call_timeout_user(&self, cv: CommunicationValue) {
|
||||||
|
let user_id = cv.get_data(DataTypes::call_id).unwrap();
|
||||||
|
let call_id = cv.get_data(DataTypes::user_id).unwrap(); // JA man braucht CALL_ID
|
||||||
|
}
|
||||||
|
async fn handle_call_disconnect_user(&self, cv: CommunicationValue) {
|
||||||
|
let user_id = cv.get_data(DataTypes::call_id).unwrap();
|
||||||
|
let call_id = cv.get_data(DataTypes::user_id).unwrap(); // JA man braucht CALL_ID
|
||||||
|
let untill = cv.get_data(DataTypes::untill).unwrap();
|
||||||
|
}
|
||||||
|
async fn handle_call_set_anonymous_joining(&self, cv: CommunicationValue) {
|
||||||
|
let call_id = cv.get_data(DataTypes::user_id).unwrap();
|
||||||
|
let enable = cv.get_data(DataTypes::enable).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/// Forward message to Iota
|
/// Forward message to Iota
|
||||||
async fn forward_to_iota(&self, cv: CommunicationValue) {
|
async fn forward_to_iota(&self, cv: CommunicationValue) {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::calls::call_group::CallGroup;
|
use crate::calls::call_group::CallGroup;
|
||||||
use crate::calls::call_manager;
|
use crate::calls::call_manager;
|
||||||
use crate::util::print::PrintType;
|
use crate::log_err;
|
||||||
use crate::util::print::line;
|
use crate::log_in;
|
||||||
use crate::util::print::line_err;
|
use crate::log_out;
|
||||||
|
use crate::util::logger::PrintType;
|
||||||
use async_tungstenite::WebSocketReceiver;
|
use async_tungstenite::WebSocketReceiver;
|
||||||
use async_tungstenite::WebSocketSender;
|
use async_tungstenite::WebSocketSender;
|
||||||
use async_tungstenite::tungstenite::Message;
|
use async_tungstenite::tungstenite::Message;
|
||||||
|
|
@ -94,17 +95,14 @@ impl IotaConnection {
|
||||||
.send(Message::Text(Utf8Bytes::from(message.to_string())))
|
.send(Message::Text(Utf8Bytes::from(message.to_string())))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
line_err(
|
log_err!(PrintType::Iota, "Failed to send WebSocket message: {:?}", e,);
|
||||||
PrintType::IotaOut,
|
|
||||||
&format!("Failed to send WebSocket message: {:?}", e),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a CommunicationValue to the Iota
|
/// Send a CommunicationValue to the Iota
|
||||||
pub async fn send_message(&self, cv: CommunicationValue) {
|
pub async fn send_message(&self, cv: CommunicationValue) {
|
||||||
if !cv.is_type(CommunicationType::pong) {
|
if !cv.is_type(CommunicationType::pong) {
|
||||||
line(PrintType::IotaOut, &cv.to_json().to_string());
|
log_out!(PrintType::Iota, "{}", cv.to_json().to_string());
|
||||||
}
|
}
|
||||||
self.send_message_str(&cv.to_json().to_string()).await;
|
self.send_message_str(&cv.to_json().to_string()).await;
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +113,7 @@ impl IotaConnection {
|
||||||
|
|
||||||
// Handle identification
|
// Handle identification
|
||||||
if cv.is_type(CommunicationType::identification) && !self.is_identified().await {
|
if cv.is_type(CommunicationType::identification) && !self.is_identified().await {
|
||||||
line(PrintType::IotaIn, &cv.to_json().to_string());
|
log_in!(PrintType::Iota, "{}", &cv.to_json().to_string());
|
||||||
self.handle_identification(cv).await;
|
self.handle_identification(cv).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +127,7 @@ impl IotaConnection {
|
||||||
self.handle_ping(cv).await;
|
self.handle_ping(cv).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
line(PrintType::IotaIn, &cv.to_json().to_string());
|
log_in!(PrintType::Iota, "{}", &cv.to_json().to_string());
|
||||||
// Handle forwarding to other Iotas or clients
|
// Handle forwarding to other Iotas or clients
|
||||||
let receiver_id = cv.get_receiver();
|
let receiver_id = cv.get_receiver();
|
||||||
if !self.get_user_ids().await.contains(&receiver_id)
|
if !self.get_user_ids().await.contains(&receiver_id)
|
||||||
|
|
@ -171,27 +169,26 @@ impl IotaConnection {
|
||||||
match id_str.parse::<i64>() {
|
match id_str.parse::<i64>() {
|
||||||
Ok(user_id) => {
|
Ok(user_id) => {
|
||||||
if let Some(auth_iota_id) = auth_connector::get_iota_id(user_id).await {
|
if let Some(auth_iota_id) = auth_connector::get_iota_id(user_id).await {
|
||||||
line(
|
log_in!(
|
||||||
PrintType::IotaIn,
|
PrintType::Iota,
|
||||||
&format!(
|
"auth for {} should be {} is {}",
|
||||||
"auth for {} should be {} is {}",
|
user_id,
|
||||||
user_id, iota_id, auth_iota_id
|
iota_id,
|
||||||
),
|
auth_iota_id
|
||||||
);
|
);
|
||||||
if auth_iota_id == iota_id {
|
if auth_iota_id == iota_id {
|
||||||
validated_user_ids.push(user_id);
|
validated_user_ids.push(user_id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
line(
|
log_in!(PrintType::Iota, "User ID {} not parsed", user_id);
|
||||||
PrintType::IotaIn,
|
|
||||||
&format!("User ID {} not parsed", id_str.trim()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
line(
|
log_in!(
|
||||||
PrintType::IotaIn,
|
PrintType::Iota,
|
||||||
&format!("Failed to parse '{}' as i64: {:?}", id_str, e),
|
"Failed to parse '{}' as i64: {:?}",
|
||||||
|
id_str,
|
||||||
|
e,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -273,16 +270,7 @@ impl IotaConnection {
|
||||||
let receiver_id = cv.get_receiver();
|
let receiver_id = cv.get_receiver();
|
||||||
let sender_id = cv.get_sender();
|
let sender_id = cv.get_sender();
|
||||||
|
|
||||||
if self.get_user_ids().await.contains(&receiver_id) {
|
if self.get_user_ids().await.contains(&sender_id) {
|
||||||
if let Some(target_rho) = self.get_rho_connection().await {
|
|
||||||
target_rho.message_to_iota(cv).await;
|
|
||||||
} else {
|
|
||||||
let error = CommunicationValue::new(CommunicationType::error)
|
|
||||||
.with_id(cv.get_id())
|
|
||||||
.with_sender(cv.get_sender());
|
|
||||||
self.send_message(error).await;
|
|
||||||
}
|
|
||||||
} else if self.get_user_ids().await.contains(&sender_id) {
|
|
||||||
if let Some(target_rho) = rho_manager::get_rho_con_for_user(receiver_id).await {
|
if let Some(target_rho) = rho_manager::get_rho_con_for_user(receiver_id).await {
|
||||||
target_rho.message_to_iota(cv).await;
|
target_rho.message_to_iota(cv).await;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -329,10 +317,10 @@ impl IotaConnection {
|
||||||
// Process contacts and add call information
|
// Process contacts and add call information
|
||||||
let enriched_contacts = if *empty {
|
let enriched_contacts = if *empty {
|
||||||
if let Some(contacts_data) = cv.get_data(DataTypes::user_ids) {
|
if let Some(contacts_data) = cv.get_data(DataTypes::user_ids) {
|
||||||
line(PrintType::CallIn, "Call empty");
|
log_in!(PrintType::Call, "Call empty");
|
||||||
contacts_data.clone()
|
contacts_data.clone()
|
||||||
} else {
|
} else {
|
||||||
line(PrintType::CallIn, "Call empty No Data");
|
log_in!(PrintType::Call, "Call empty No Data");
|
||||||
JsonValue::new_array()
|
JsonValue::new_array()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::rho_connection::RhoConnection;
|
use super::rho_connection::RhoConnection;
|
||||||
use crate::util::print::PrintType;
|
use crate::log_in;
|
||||||
use crate::util::print::line;
|
use crate::log_out;
|
||||||
|
use crate::util::logger::PrintType;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::{Arc, LazyLock},
|
sync::{Arc, LazyLock},
|
||||||
|
|
@ -12,17 +13,12 @@ pub static RHO_CONNECTIONS: LazyLock<Arc<RwLock<HashMap<i64, Arc<RhoConnection>>
|
||||||
|
|
||||||
pub async fn get_rho_con_for_user(user_id: i64) -> Option<Arc<RhoConnection>> {
|
pub async fn get_rho_con_for_user(user_id: i64) -> Option<Arc<RhoConnection>> {
|
||||||
let connections = RHO_CONNECTIONS.read().await;
|
let connections = RHO_CONNECTIONS.read().await;
|
||||||
line(
|
log_in!(PrintType::Client, "Checking user ID: {:?}", user_id,);
|
||||||
PrintType::ClientIn,
|
|
||||||
&format!("Checking user ID: {:?}", user_id),
|
|
||||||
);
|
|
||||||
for rho_connection in connections.values() {
|
for rho_connection in connections.values() {
|
||||||
line(
|
log_in!(
|
||||||
PrintType::ClientIn,
|
PrintType::Client,
|
||||||
&format!(
|
"Comparing user IDs: {:?}",
|
||||||
"Comparing user IDs: {:?}",
|
rho_connection.get_user_ids().to_vec()
|
||||||
rho_connection.get_user_ids().to_vec()
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if rho_connection.get_user_ids().contains(&user_id) {
|
if rho_connection.get_user_ids().contains(&user_id) {
|
||||||
return Some(Arc::clone(rho_connection));
|
return Some(Arc::clone(rho_connection));
|
||||||
|
|
|
||||||
219
src/util/logger.rs
Normal file
219
src/util/logger.rs
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
use std::{
|
||||||
|
fs::{self, OpenOptions},
|
||||||
|
io::Write,
|
||||||
|
path::Path,
|
||||||
|
sync::{OnceLock, mpsc},
|
||||||
|
thread,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
|
use ansi_term::Color;
|
||||||
|
|
||||||
|
static LOGGER: OnceLock<mpsc::Sender<LogMessage>> = OnceLock::new();
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum PrintType {
|
||||||
|
Call,
|
||||||
|
Client,
|
||||||
|
Iota,
|
||||||
|
Omikron,
|
||||||
|
Omega,
|
||||||
|
General,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LogMessage {
|
||||||
|
timestamp_ms: u128,
|
||||||
|
sender: Option<i64>,
|
||||||
|
prefix: &'static str,
|
||||||
|
kind: PrintType,
|
||||||
|
is_error: bool,
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the logging subsystem.
|
||||||
|
/// Must be called exactly once during startup.
|
||||||
|
pub fn startup() {
|
||||||
|
let (tx, rx) = mpsc::channel::<LogMessage>();
|
||||||
|
LOGGER.set(tx).expect("Logger already initialized");
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let log_dir = Path::new("logs");
|
||||||
|
fs::create_dir_all(log_dir).expect("Failed to create log directory");
|
||||||
|
|
||||||
|
let start_ts = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs();
|
||||||
|
|
||||||
|
let path = log_dir.join(format!("log_{}.txt", start_ts));
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(path)
|
||||||
|
.expect("Failed to open log file");
|
||||||
|
|
||||||
|
for msg in rx {
|
||||||
|
let ts = fixed_box(&msg.timestamp_ms.to_string(), 13);
|
||||||
|
let sender = match msg.sender {
|
||||||
|
Some(id) => fixed_box(&id.to_string(), 19),
|
||||||
|
None => fixed_box("", 19),
|
||||||
|
};
|
||||||
|
|
||||||
|
let line = format!("{} {} {} {}", ts, sender, msg.prefix, msg.message);
|
||||||
|
|
||||||
|
// Console (ANSI-colored)
|
||||||
|
println!("{}", colorize(msg.kind, msg.is_error).paint(&line));
|
||||||
|
|
||||||
|
// File (plain text)
|
||||||
|
let _ = writeln!(file, "{}", line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn colorize(kind: PrintType, is_error: bool) -> Color {
|
||||||
|
if is_error {
|
||||||
|
return Color::Red;
|
||||||
|
}
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
PrintType::Call => Color::Purple,
|
||||||
|
PrintType::Client => Color::Green,
|
||||||
|
PrintType::Iota => Color::Yellow,
|
||||||
|
PrintType::Omikron => Color::Blue,
|
||||||
|
PrintType::Omega => Color::Cyan,
|
||||||
|
PrintType::General => Color::White,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fixed_box(content: &str, width: usize) -> String {
|
||||||
|
let s: String = content.chars().take(width).collect();
|
||||||
|
let len = s.chars().count();
|
||||||
|
if len < width {
|
||||||
|
format!("[{}{}]", " ".repeat(width - len), s)
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Internal async logging entry point.
|
||||||
|
* Not exposed publicly; all access goes through macros.
|
||||||
|
*/
|
||||||
|
pub fn log_internal(
|
||||||
|
sender: Option<i64>,
|
||||||
|
kind: PrintType,
|
||||||
|
prefix: &'static str,
|
||||||
|
is_error: bool,
|
||||||
|
message: String,
|
||||||
|
) {
|
||||||
|
if let Some(tx) = LOGGER.get() {
|
||||||
|
let _ = tx.send(LogMessage {
|
||||||
|
timestamp_ms: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_millis(),
|
||||||
|
sender,
|
||||||
|
prefix,
|
||||||
|
kind,
|
||||||
|
is_error,
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Log a general informational message.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log {
|
||||||
|
|
||||||
|
// actor only
|
||||||
|
($kind:expr, $($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(None, $kind, "", false, format!($($arg)*))
|
||||||
|
};
|
||||||
|
|
||||||
|
// sender + actor
|
||||||
|
($sender:expr, $kind:expr, $($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(Some($sender), $kind, "", false, format!($($arg)*))
|
||||||
|
};
|
||||||
|
|
||||||
|
// plain
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(
|
||||||
|
None,
|
||||||
|
$crate::util::logger::PrintType::General,
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
format!($($arg)*)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// Log an inbound message (`>`).
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_in {
|
||||||
|
// actor only
|
||||||
|
($kind:expr, $($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(None, $kind, ">", false, format!($($arg)*))
|
||||||
|
};
|
||||||
|
|
||||||
|
// sender + actor
|
||||||
|
($sender:expr, $kind:expr, $($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(Some($sender), $kind, ">", false, format!($($arg)*))
|
||||||
|
};
|
||||||
|
|
||||||
|
// plain
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(
|
||||||
|
None,
|
||||||
|
$crate::util::logger::PrintType::General,
|
||||||
|
">",
|
||||||
|
false,
|
||||||
|
format!($($arg)*)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// Log an outbound message (`<`).
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_out {
|
||||||
|
// actor only
|
||||||
|
($kind:expr, $($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(None, $kind, "<", false, format!($($arg)*))
|
||||||
|
};
|
||||||
|
|
||||||
|
// sender + actor
|
||||||
|
($sender:expr, $kind:expr, $($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(Some($sender), $kind, "<", false, format!($($arg)*))
|
||||||
|
};
|
||||||
|
|
||||||
|
// plain
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(
|
||||||
|
None,
|
||||||
|
$crate::util::logger::PrintType::General,
|
||||||
|
"<",
|
||||||
|
false,
|
||||||
|
format!($($arg)*)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// Log an error message (`>>`).
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_err {
|
||||||
|
|
||||||
|
// actor only
|
||||||
|
($kind:expr, $($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(None, $kind, ">>", true, format!($($arg)*))
|
||||||
|
};
|
||||||
|
|
||||||
|
// sender + actor
|
||||||
|
($sender:expr, $kind:expr, $($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(Some($sender), $kind, ">>", true, format!($($arg)*))
|
||||||
|
};
|
||||||
|
|
||||||
|
// plain
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::util::logger::log_internal(
|
||||||
|
None,
|
||||||
|
$crate::util::logger::PrintType::General,
|
||||||
|
">>",
|
||||||
|
true,
|
||||||
|
format!($($arg)*)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
pub mod config_util;
|
pub mod config_util;
|
||||||
pub mod file_util;
|
pub mod file_util;
|
||||||
pub mod print;
|
pub mod logger;
|
||||||
|
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
use ansi_term::Color;
|
|
||||||
|
|
||||||
pub fn print_start_message() {
|
|
||||||
println!("{}", Color::Yellow.paint("> Iota inbound"));
|
|
||||||
println!("{}", Color::Purple.paint("< Iota outbound"));
|
|
||||||
println!("{}", Color::Green.paint("> Client inbound"));
|
|
||||||
println!("{}", Color::Blue.paint("< Client outbound"));
|
|
||||||
println!("{}", Color::Red.paint("> Call inbound"));
|
|
||||||
println!("{}", Color::Red.paint("< Call outbound"));
|
|
||||||
println!("{}", Color::Cyan.paint("> Omega inbound"));
|
|
||||||
println!("{}", Color::Cyan.paint("< Omega outbound"));
|
|
||||||
println!("{}", Color::White.paint("General info"));
|
|
||||||
println!("{}", Color::White.paint(">> Erros"));
|
|
||||||
}
|
|
||||||
pub enum PrintType {
|
|
||||||
IotaIn,
|
|
||||||
IotaOut,
|
|
||||||
OmegaIn,
|
|
||||||
OmegaOut,
|
|
||||||
ClientIn,
|
|
||||||
ClientOut,
|
|
||||||
CallIn,
|
|
||||||
CallOut,
|
|
||||||
General,
|
|
||||||
}
|
|
||||||
pub fn line(key: PrintType, message: &str) {
|
|
||||||
match key {
|
|
||||||
PrintType::IotaIn => println!(
|
|
||||||
"{}{}",
|
|
||||||
Color::Yellow.paint(">"),
|
|
||||||
Color::Yellow.paint(message)
|
|
||||||
),
|
|
||||||
PrintType::IotaOut => println!(
|
|
||||||
"{}{}",
|
|
||||||
Color::Purple.paint("<"),
|
|
||||||
Color::Purple.paint(message)
|
|
||||||
),
|
|
||||||
PrintType::OmegaIn => println!("{}{}", Color::Cyan.paint(">"), Color::Cyan.paint(message)),
|
|
||||||
PrintType::OmegaOut => println!("{}{}", Color::Cyan.paint("<"), Color::Cyan.paint(message)),
|
|
||||||
PrintType::ClientIn => {
|
|
||||||
println!("{}{}", Color::Green.paint(">"), Color::Green.paint(message))
|
|
||||||
}
|
|
||||||
PrintType::ClientOut => {
|
|
||||||
println!("{}{}", Color::Blue.paint("<"), Color::Blue.paint(message))
|
|
||||||
}
|
|
||||||
PrintType::CallIn => {
|
|
||||||
println!("{}{}", Color::Red.paint(">"), Color::Red.paint(message))
|
|
||||||
}
|
|
||||||
PrintType::CallOut => {
|
|
||||||
println!("{}{}", Color::Red.paint("<"), Color::Red.paint(message))
|
|
||||||
}
|
|
||||||
PrintType::General => println!("{}", Color::White.paint(message)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn line_err(key: PrintType, message: &str) {
|
|
||||||
match key {
|
|
||||||
PrintType::IotaIn => println!(
|
|
||||||
"{}{}",
|
|
||||||
Color::Yellow.paint(">>"),
|
|
||||||
Color::Yellow.paint(message)
|
|
||||||
),
|
|
||||||
PrintType::IotaOut => println!(
|
|
||||||
"{}{}",
|
|
||||||
Color::Purple.paint("<<"),
|
|
||||||
Color::Purple.paint(message)
|
|
||||||
),
|
|
||||||
PrintType::OmegaIn => println!("{}{}", Color::Cyan.paint(">>"), Color::Cyan.paint(message)),
|
|
||||||
PrintType::OmegaOut => {
|
|
||||||
println!("{}{}", Color::Cyan.paint("<<"), Color::Cyan.paint(message))
|
|
||||||
}
|
|
||||||
PrintType::ClientIn => println!(
|
|
||||||
"{}{}",
|
|
||||||
Color::Green.paint(">>"),
|
|
||||||
Color::Green.paint(message)
|
|
||||||
),
|
|
||||||
PrintType::ClientOut => {
|
|
||||||
println!("{}{}", Color::Blue.paint("<<"), Color::Blue.paint(message))
|
|
||||||
}
|
|
||||||
PrintType::CallIn => println!("{}{}", Color::Red.paint(">>"), Color::Red.paint(message)),
|
|
||||||
PrintType::CallOut => {
|
|
||||||
println!("{}{}", Color::Red.paint("<<"), Color::Red.paint(message))
|
|
||||||
}
|
|
||||||
PrintType::General => println!("{}", Color::Red.paint(message)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue