This commit is contained in:
Alex Emmet 2026-01-29 12:11:00 +01:00
commit 0990ccd526
16 changed files with 139 additions and 393 deletions

View file

@ -12,10 +12,7 @@ use uuid::Uuid;
use crate::anonymous_clients::anonymous_manager::generate_username;
use crate::calls::call_manager;
use crate::data::{
communication::{CommunicationType, CommunicationValue, DataTypes},
user::User,
};
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
use crate::omega::omega_connection::{WAITING_TASKS, get_omega_connection};
use crate::rho::rho_manager;
use crate::util::logger::PrintType;
@ -352,7 +349,7 @@ impl AnonymousClientConnection {
return;
}
},
None => {
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::error_no_call_id)
.await;
return;
@ -370,7 +367,7 @@ impl AnonymousClientConnection {
// Find target RhoConnection
let target_rho = match rho_manager::get_rho_con_for_user(receiver_id).await {
Some(rho) => rho,
None => {
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::error)
.await;
return;
@ -407,7 +404,7 @@ impl AnonymousClientConnection {
return;
}
},
None => {
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::error)
.await;
return;
@ -521,15 +518,12 @@ impl AnonymousClientConnection {
}
/// Check if interested in a user and send notification
pub async fn are_you_interested(self: Arc<Self>, user: &User) {
pub async fn are_you_interested(self: Arc<Self>, user_id: i64) {
let interested_guard = self.clone().get_interested_users().await;
if interested_guard.contains(&user.user_id) {
if interested_guard.contains(&user_id) {
let notification = CommunicationValue::new(CommunicationType::client_changed)
.add_data_str(DataTypes::user_id, user.user_id.to_string())
.add_data_str(
DataTypes::user_state,
format!("{:?}", user.status.to_string()),
);
.add_data_str(DataTypes::user_id, user_id.to_string())
.add_data_str(DataTypes::user_state, format!("online"));
self.send_message(&notification).await;
}

View file

@ -1,17 +1,11 @@
use dashmap::DashMap;
use livekit::Room;
use livekit_api::services::room::RoomClient;
use once_cell::sync::Lazy;
use std::{env, str::FromStr, sync::Arc, time::Duration};
use std::sync::Arc;
use uuid::Uuid;
use crate::{
calls::{call_group::CallGroup, caller::Caller},
log, log_err,
util::logger::PrintType,
};
use crate::calls::{call_group::CallGroup, caller::Caller};
static CALL_GROUPS: Lazy<DashMap<Uuid, Arc<CallGroup>>> = Lazy::new(|| DashMap::new());
pub static CALL_GROUPS: Lazy<DashMap<Uuid, Arc<CallGroup>>> = Lazy::new(|| DashMap::new());
pub async fn get_call_invites(user_id: i64) -> Vec<Arc<Caller>> {
let mut callers = Vec::new();
@ -101,59 +95,3 @@ pub async fn add_invite(call_id: Uuid, inviter_id: i64, invitee_id: i64) -> bool
}
false
}
pub fn garbage_collect_calls() {
tokio::spawn(async move {
loop {
clean_calls().await;
tokio::time::sleep(Duration::from_secs(2)).await;
}
});
}
pub async fn clean_calls() {
let api_key = match env::var("LIVEKIT_API_KEY") {
Ok(key) => key,
Err(_) => {
log_err!(PrintType::General, "LIVEKIT_API_KEY not set!");
return;
}
};
let api_secret = match env::var("LIVEKIT_API_SECRET") {
Ok(secret) => secret,
Err(_) => {
log_err!(PrintType::General, "LIVEKIT_API_SECRET not set!");
return;
}
};
let room_service = RoomClient::with_api_key("https:call.tensamin.net", &api_key, &api_secret);
let rooms = room_service.list_rooms(Vec::new()).await.unwrap();
let mut call_ids: Vec<Uuid> = Vec::new();
let mut no_users: Vec<Uuid> = Vec::new();
for room in rooms {
if let Ok(id) = Uuid::from_str(&room.name) {
if room.num_participants == 0 {
no_users.push(id);
}
call_ids.push(id);
}
}
let size_pre = CALL_GROUPS.len();
for (id, _) in CALL_GROUPS.clone().into_iter() {
if !call_ids.contains(&id) {
CALL_GROUPS.remove(&id);
}
}
for (_, cg) in CALL_GROUPS.clone().into_iter() {
*cg.show.write().await = !no_users.contains(&cg.call_id);
}
let size_post = CALL_GROUPS.len();
if size_pre - size_post != 0 {
log!(
PrintType::Call,
"Cleaned {} calls, {} remaining",
size_pre - size_post,
size_post
);
}
}

View file

@ -1,14 +1,29 @@
use livekit_api::access_token;
use livekit_api::{
access_token::{self},
services::room::RoomClient,
};
use std::env;
use std::str::FromStr;
use std::time::Duration;
use uuid::Uuid;
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_secret = env::var("LIVEKIT_API_SECRET").expect("LIVEKIT_API_SECRET is not set");
use crate::{calls::call_manager::CALL_GROUPS, log, log_err, util::logger::PrintType};
pub fn create_token(user_id: i64, call_id: Uuid, has_admin: bool) -> Result<String, ()> {
let api_key = match env::var("LIVEKIT_API_KEY") {
Ok(key) => key,
Err(_) => {
log_err!(PrintType::General, "LIVEKIT_API_KEY not set!");
return Err(());
}
};
let api_secret = match env::var("LIVEKIT_API_SECRET") {
Ok(secret) => secret,
Err(_) => {
log_err!(PrintType::General, "LIVEKIT_API_SECRET not set!");
return Err(());
}
};
let token = access_token::AccessToken::with_api_key(&api_key, &api_secret)
.with_identity(&user_id.to_string())
@ -21,5 +36,72 @@ pub fn create_token(
})
.with_metadata(&format!("{{\"isAdmin\":{}}}", has_admin))
.to_jwt();
return token;
if let Ok(token) = token {
Ok(token)
} else {
Err(())
}
}
pub fn garbage_collect_calls() {
tokio::spawn(async move {
let api_key = match env::var("LIVEKIT_API_KEY") {
Ok(key) => key,
Err(_) => {
log_err!(PrintType::General, "LIVEKIT_API_KEY not set!");
return;
}
};
let api_secret = match env::var("LIVEKIT_API_SECRET") {
Ok(secret) => secret,
Err(_) => {
log_err!(PrintType::General, "LIVEKIT_API_SECRET not set!");
return;
}
};
let hostname = match env::var("LIVEKI_HOSTNAME") {
Ok(secret) => secret,
Err(_) => {
log_err!(PrintType::General, "LIVEKI_HOSTNAME not set!");
return;
}
};
loop {
let room_service = RoomClient::with_api_key(&hostname, &api_key, &api_secret);
clean_calls(room_service).await;
tokio::time::sleep(Duration::from_secs(2)).await;
}
});
}
pub async fn clean_calls(room_service: RoomClient) {
let rooms = room_service.list_rooms(Vec::new()).await.unwrap();
let mut call_ids: Vec<Uuid> = Vec::new();
let mut no_users: Vec<Uuid> = Vec::new();
for room in rooms {
if let Ok(id) = Uuid::from_str(&room.name) {
if room.num_participants == 0 {
no_users.push(id);
}
call_ids.push(id);
}
}
let size_pre = CALL_GROUPS.len();
for (id, _) in CALL_GROUPS.clone().into_iter() {
if !call_ids.contains(&id) {
CALL_GROUPS.remove(&id);
}
}
for (_, cg) in CALL_GROUPS.clone().into_iter() {
*cg.show.write().await = !no_users.contains(&cg.call_id);
}
let size_post = CALL_GROUPS.len();
if size_pre - size_post != 0 {
log!(
PrintType::Call,
"Cleaned {} calls, {} remaining",
size_pre - size_post,
size_post
);
}
}

View file

@ -27,7 +27,7 @@ impl Caller {
pub fn has_admin(&self) -> bool {
self.has_admin
}
pub async fn is_timeout(&self) -> bool {
pub async fn is_timeouted(&self) -> bool {
*self.timeout.read().await
> SystemTime::now()
.duration_since(UNIX_EPOCH)

View file

@ -1,19 +1,5 @@
#[derive(Clone)]
pub struct User {
pub iota_id: i64,
pub user_id: i64,
pub status: UserStatus,
}
impl User {
pub fn new(iota_id: i64, user_id: i64, status: UserStatus) -> Self {
User {
iota_id,
user_id,
status,
}
}
}
#[derive(Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum UserStatus {
online,
do_not_disturb,

View file

@ -18,11 +18,10 @@ use crate::{
anonymous_clients::{
anonymous_client_connection::AnonymousClientConnection, anonymous_manager,
},
calls::call_manager::garbage_collect_calls,
calls::call_util::garbage_collect_calls,
omega::omega_connection::OmegaConnection,
rho::{client_connection::ClientConnection, iota_connection::IotaConnection},
util::{
config_util::CONFIG,
crypto_helper::{load_public_key, load_secret_key},
logger::{PrintType, startup},
},
@ -44,7 +43,11 @@ async fn main() {
Arc::new(OmegaConnection::new()).connect();
});
startup();
let address = format!("{}:{}", &CONFIG.read().await.ip, &CONFIG.read().await.port);
let address = format!(
"{}:{}",
env::var("IP").unwrap_or("0.0.0.0".to_string()),
env::var("PORT").unwrap_or("959".to_string())
);
let listener = TcpListener::bind(&address).await.unwrap();
log!(
@ -97,7 +100,7 @@ async fn main() {
client_conn.handle_close().await;
return;
}
None => {
_ => {
log_in!(PrintType::Client, "Client stream ended");
client_conn.handle_close().await;
return;
@ -139,7 +142,7 @@ async fn main() {
client_conn.handle_close().await;
return;
}
None => {
_ => {
log_in!(PrintType::Client, "Anonymous Client stream ended");
anonymous_manager::remove_anonymous_user(
client_conn.get_user_id().await,
@ -176,7 +179,7 @@ async fn main() {
iota_conn.handle_close().await;
return;
}
None => {
_ => {
// Stream ended
log_in!(PrintType::Iota, "Iota stream ended");
iota_conn.handle_close().await;

View file

@ -17,17 +17,19 @@ use tokio::{
use tokio_native_tls::TlsStream;
use uuid::Uuid;
use crate::log_err;
use crate::{
data::{
communication::{CommunicationType, CommunicationValue, DataTypes},
user::UserStatus,
},
get_private_key, log, log_in, log_out,
rho::rho_manager::{self, RHO_CONNECTIONS},
util::crypto_helper::{decrypt_b64, secret_key_to_base64},
util::logger::PrintType,
rho::rho_manager::{self, RHO_CONNECTIONS, connection_count},
util::{
crypto_helper::{decrypt_b64, secret_key_to_base64},
logger::PrintType,
},
};
use crate::{log_err, util::crypto_helper::load_public_key};
pub static WAITING_TASKS: Lazy<
DashMap<Uuid, Box<dyn Fn(Arc<OmegaConnection>, CommunicationValue) -> bool + Send + Sync>>,
@ -159,8 +161,6 @@ impl OmegaConnection {
.to_string()
})?;
let server_pub_key_obj = load_public_key(server_pub_key).unwrap();
let decrypted_challenge = decrypt_b64(
&secret_key_to_base64(&get_private_key()),
server_pub_key,
@ -222,7 +222,8 @@ impl OmegaConnection {
let sync_msg = CommunicationValue::new(CommunicationType::sync_client_iota_status)
.add_data(DataTypes::iota_ids, JsonValue::Array(connected_iota_ids))
.add_data(DataTypes::user_ids, JsonValue::Array(connected_user_ids));
.add_data(DataTypes::user_ids, JsonValue::Array(connected_user_ids))
.add_data(DataTypes::rho_connections, JsonValue::from(connection_count().await));
selfc.send_message(&sync_msg).await;
});
@ -340,12 +341,6 @@ impl OmegaConnection {
}
}
pub async fn connect_iota(iota_id: i64, _user_ids: Vec<i64>) {
let cv = CommunicationValue::new(CommunicationType::iota_connected)
.add_data(DataTypes::iota_id, JsonValue::from(iota_id));
OmegaConnection::send_global(cv).await;
}
pub async fn close_iota(iota_id: i64) {
let cv = CommunicationValue::new(CommunicationType::iota_disconnected)
.add_data(DataTypes::iota_id, JsonValue::from(iota_id));
@ -459,7 +454,7 @@ impl OmegaConnection {
match tokio::time::timeout(timeout, rx.recv()).await {
Ok(Some(response_cv)) => Ok(response_cv),
Ok(None) => Err("Failed to receive response, channel was closed.".to_string()),
Ok(_) => Err("Failed to receive response, channel was closed.".to_string()),
Err(_) => {
WAITING_TASKS.remove(&msg_id);
Err(format!(

View file

@ -20,10 +20,9 @@ use crate::util::crypto_helper::{load_public_key, public_key_to_base64};
use crate::util::crypto_util::{DataFormat, SecurePayload};
use crate::util::logger::PrintType;
use crate::{
// calls::call_manager::CallManager,
data::{
communication::{CommunicationType, CommunicationValue, DataTypes},
user::{User, UserStatus},
user::UserStatus,
},
omega::omega_connection::OmegaConnection,
};
@ -163,7 +162,7 @@ impl ClientConnection {
let pub_key = match load_public_key(base64_pub) {
Some(pk) => pk,
None => {
_ => {
self.clone()
.send_error_response(
&cv.get_id(),
@ -227,7 +226,7 @@ impl ClientConnection {
let rho_connection = match rho_manager::get_rho_con_for_user(user_id).await {
Some(rho) => rho,
None => {
_ => {
self.send_error_response(
&cv.get_id(),
CommunicationType::error_no_iota,
@ -432,7 +431,7 @@ impl ClientConnection {
return;
}
},
None => {
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::error_no_call_id)
.await;
return;
@ -450,7 +449,7 @@ impl ClientConnection {
// Find target RhoConnection
let target_rho = match rho_manager::get_rho_con_for_user(receiver_id).await {
Some(rho) => rho,
None => {
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::error)
.await;
return;
@ -487,7 +486,7 @@ impl ClientConnection {
return;
}
},
None => {
_ => {
self.send_error_response(&cv.get_id(), CommunicationType::error)
.await;
return;
@ -697,15 +696,12 @@ impl ClientConnection {
}
/// Check if interested in a user and send notification
pub async fn are_you_interested(self: Arc<Self>, user: &User) {
pub async fn are_you_interested(self: Arc<Self>, user_id: i64) {
let interested_guard = self.clone().get_interested_users().await;
if interested_guard.contains(&user.user_id) {
if interested_guard.contains(&user_id) {
let notification = CommunicationValue::new(CommunicationType::client_changed)
.add_data_str(DataTypes::user_id, user.user_id.to_string())
.add_data_str(
DataTypes::user_state,
format!("{:?}", user.status.to_string()),
);
.add_data_str(DataTypes::user_id, user_id.to_string())
.add_data_str(DataTypes::user_state, format!("online"));
self.send_message(&notification).await;
}

View file

@ -189,7 +189,7 @@ impl IotaConnection {
let pub_key = match load_public_key(base64_pub) {
Some(pk) => pk,
None => {
_ => {
self.send_error_response(
&cv.get_id(),
CommunicationType::error_invalid_public_key,
@ -594,6 +594,7 @@ impl IotaConnection {
}
}
}
pub async fn await_response(
&self,
cv: &CommunicationValue,
@ -626,7 +627,7 @@ impl IotaConnection {
match tokio::time::timeout(timeout, rx.recv()).await {
Ok(Some(response_cv)) => Ok(response_cv),
Ok(None) => Err("Failed to receive response, channel was closed.".to_string()),
Ok(_) => Err("Failed to receive response, channel was closed.".to_string()),
Err(_) => {
self.waiting_tasks.remove(&msg_id);
Err(format!(

View file

@ -147,10 +147,10 @@ impl RhoConnection {
}
/// Check if clients are interested in a user
pub async fn are_they_interested(&self, user: &crate::data::user::User) {
pub async fn are_they_interested(&self, user_id: i64) {
let connections = self.client_connections.read().await;
for connection in connections.iter() {
connection.clone().are_you_interested(user).await;
connection.clone().are_you_interested(user_id).await;
}
}

View file

@ -50,12 +50,6 @@ pub async fn get_rho_by_iota(iota_id: i64) -> Option<Arc<RhoConnection>> {
connections.get(&iota_id).map(Arc::clone)
}
/// Get all active RhoConnections
pub async fn get_all_connections() -> Vec<Arc<RhoConnection>> {
let connections = RHO_CONNECTIONS.read().await;
connections.values().map(Arc::clone).collect()
}
/// Get the count of active connections
pub async fn connection_count() -> usize {
let connections = RHO_CONNECTIONS.read().await;

View file

@ -1,58 +0,0 @@
use crate::util::file_util::load_file;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub omega_server: String,
pub auth_server: String,
pub omikron_id: Uuid,
pub keep_people_stored_for: i32,
pub max_data: u64,
pub ip: String,
pub port: u16,
}
impl Default for Config {
fn default() -> Self {
Self {
omega_server: "omega.tensamin.net".into(),
auth_server: "auth.tensamin.net".into(),
omikron_id: Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap_or_default(),
keep_people_stored_for: 90,
max_data: 1000 * 1000 * 1000 * 8,
ip: "0.0.0.0".into(),
port: 959,
}
}
}
pub static CONFIG: Lazy<RwLock<Config>> = Lazy::new(|| RwLock::new(Config::load()));
impl Config {
pub fn load() -> Self {
let content = load_file("", "config.json");
if content.trim().is_empty() {
return Config::default();
}
let json = json::parse(&content).unwrap();
Self {
omega_server: json["omega_server"]
.as_str()
.unwrap_or("omega.tensamin.net")
.into(),
auth_server: json["auth_server"]
.as_str()
.unwrap_or("auth.tensamin.net")
.into(),
omikron_id: Uuid::parse_str(json["omikron_id"].as_str().unwrap_or_default())
.unwrap_or_default(),
keep_people_stored_for: json["keep_people_stored_for"].as_i64().unwrap_or(90) as i32,
max_data: json["max_data"].as_u64().unwrap_or(8000000000),
ip: json["ip"].as_str().unwrap_or("0.0.0.0").into(),
port: json["port"].as_u64().unwrap_or(959) as u16,
}
}
}

View file

@ -25,11 +25,8 @@ pub enum DataFormat {
Hex,
}
// --- Main Class Structure ---
pub struct SecurePayload {
/// The internal canonical representation is always raw bytes.
inner_data: Vec<u8>,
/// The private key of the user associated with this payload instance.
private_key: Secret,
}
@ -109,22 +106,14 @@ impl SecurePayload {
let peer_pub = public_key.into();
let shared_secret = self.private_key.as_diffie_hellman(&peer_pub).unwrap();
println!(
"Encryption Shared Secret (Hex): {}",
hex::encode(shared_secret.as_bytes())
);
// 3. Key & Nonce Derivation (HKDF)
// We derive 32 bytes for the key and 12 bytes for a deterministic nonce.
let hkdf = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
let mut okm = [0u8; 44]; // 32 (Key) + 12 (Nonce)
let mut okm = [0u8; 44];
hkdf.expand(b"x448-aes-gcm-no-overhead", &mut okm)
.map_err(|_| SecurePayloadError::EncryptionError)?;
let key = &okm[..32];
let nonce_bytes = &okm[32..];
// 4. Encrypt with AES-256-GCM
let cipher = Aes256Gcm::new(key.into());
let nonce = Nonce::from_slice(nonce_bytes);
@ -138,7 +127,6 @@ impl SecurePayload {
)
.map_err(|_| SecurePayloadError::EncryptionError)?;
// 5. Result is ONLY the ciphertext. No key or nonce is packed.
Ok(SecurePayload {
inner_data: ciphertext,
private_key: Secret::from_bytes(self.private_key.as_bytes()).unwrap(),
@ -160,17 +148,9 @@ impl SecurePayload {
&self,
peer_public_key_bytes: &[u8; 56],
) -> Result<SecurePayload, SecurePayloadError> {
// 1. Perform Exchange
let peer_pub = PublicKey::from_bytes(peer_public_key_bytes).unwrap();
let shared_secret = self.private_key.as_diffie_hellman(&peer_pub).unwrap();
// LOGGING: Shared Secret
println!(
"Decryption Shared Secret (Hex): {}",
hex::encode(shared_secret.as_bytes())
);
// 2. Key & Nonce Derivation (Must match encryption exactly)
let hkdf = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
let mut okm = [0u8; 44];
hkdf.expand(b"x448-aes-gcm-no-overhead", &mut okm)
@ -179,7 +159,6 @@ impl SecurePayload {
let key = &okm[..32];
let nonce_bytes = &okm[32..];
// 3. Decrypt with AES-256-GCM
let cipher = Aes256Gcm::new(key.into());
let nonce = Nonce::from_slice(nonce_bytes);

View file

@ -1,162 +0,0 @@
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::Read;
use std::path::{Path, PathBuf};
use sysinfo::System;
use uuid::Uuid;
use walkdir::WalkDir;
pub fn delete_file(path: &str, name: &str) -> bool {
let dir = Path::new(&get_directory()).join(path);
let file = dir.join(name);
if !file.exists() {
return false;
}
fs::remove_file(file).is_ok()
}
pub fn delete_directory(path: &str) -> bool {
let dir = Path::new(&get_directory()).join(path);
delete_dir_recursive(&dir)
}
fn delete_dir_recursive(directory: &Path) -> bool {
if !directory.exists() {
return false;
}
if let Err(e) = fs::remove_dir_all(directory) {
println!(
"[IMPORTANT] Couldn't delete directory {}: {}",
directory.display(),
e
);
return false;
}
true
}
pub fn delete_user_directory(user_id: Uuid) {
let user_dir = Path::new(&get_directory())
.join("users")
.join(user_id.to_string());
let _ = delete_dir_recursive(&user_dir);
}
pub fn load_file(path: &str, name: &str) -> String {
let dir = Path::new(&get_directory()).join(path);
let file_path = dir.join(name);
if !dir.exists() {
if let Err(e) = fs::create_dir_all(&dir) {
println!("[IMPORTANT] Couldn't create directories: {}", e);
return String::new();
}
return String::new();
}
if !file_path.exists() {
if let Err(e) = File::create(&file_path) {
println!("[IMPORTANT] Couldn't create file: {}", e);
}
return String::new();
}
let mut content = String::new();
if let Ok(mut f) = File::open(&file_path) {
let _ = f.read_to_string(&mut content);
}
content
}
pub fn save_file(path: &str, name: &str, value: &str) {
let dir = Path::new(&get_directory()).join(path);
let file_path = dir.join(name);
if !dir.exists() {
if let Err(e) = fs::create_dir_all(&dir) {
println!("[IMPORTANT] Couldn't create directories: {}", e);
return;
}
}
if let Err(e) = fs::write(&file_path, value) {
println!(
"[IMPORTANT] Couldn't write file {}: {}",
file_path.display(),
e
);
}
}
pub fn get_children(path: &str) -> Vec<String> {
let dir = Path::new(&get_directory()).join(path);
let mut children = Vec::new();
if let Ok(entries) = fs::read_dir(&dir) {
for entry in entries {
if let Ok(entry) = entry {
children.push(entry.file_name().to_string_lossy().to_string());
}
}
}
children
}
pub fn get_directory() -> String {
let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from("."));
exe.parent()
.unwrap_or(Path::new("."))
.to_string_lossy()
.to_string()
}
pub fn used_space() -> u64 {
get_directory_size(&PathBuf::from(get_directory()))
}
pub fn get_directory_size(directory: &Path) -> u64 {
let mut size = 0;
for entry in WalkDir::new(directory).into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.is_file() {
if let Ok(metadata) = path.metadata() {
size += path.file_name().unwrap_or(OsStr::new("")).len() as u64;
size += metadata.len();
}
}
}
size
}
pub fn get_designed_storage(user_id: Uuid) -> String {
let user_dir = Path::new(&get_directory())
.join("users")
.join(user_id.to_string());
design_byte(get_directory_size(&user_dir))
}
pub fn design_byte(bytes: u64) -> String {
let mut hr_size = format!("{:.2}B", bytes as f64);
let k = bytes as f64 / 1024.0;
let m = k / 1024.0;
let g = m / 1024.0;
let t = g / 1024.0;
if t >= 1.0 {
hr_size = format!("{:.2}TB", t);
} else if g >= 1.0 {
hr_size = format!("{:.2}GB", g);
} else if m >= 1.0 {
hr_size = format!("{:.2}MB", m);
} else if k >= 1.0 {
hr_size = format!("{:.2}KB", k);
}
hr_size
}
pub fn get_used_ram() -> String {
let mut sys = System::new_all();
sys.refresh_all();
let used = sys.used_memory() * 1024; // kB to bytes
let total = sys.total_memory() * 1024;
format!("{}/{}", design_byte(used), design_byte(total))
}

View file

@ -56,7 +56,7 @@ pub fn startup() {
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),
_ => fixed_box("", 19),
};
let line = format!("{} {} {} {}", ts, sender, msg.prefix, msg.message);

View file

@ -1,5 +1,3 @@
pub mod config_util;
pub mod crypto_helper;
pub mod crypto_util;
pub mod file_util;
pub mod logger;