This commit is contained in:
Alex Emmet 2025-11-14 11:11:18 +01:00
commit 258404b7fa
14 changed files with 109 additions and 54 deletions

View file

@ -1,12 +1,11 @@
use aes_gcm::{ use aes_gcm::{
Aes256Gcm, // AES256 GCM Aes256Gcm, Nonce,
Nonce,
aead::{Aead, KeyInit, OsRng}, aead::{Aead, KeyInit, OsRng},
}; };
use base64::{Engine as _, engine::general_purpose::STANDARD}; use base64::{Engine as _, engine::general_purpose::STANDARD};
use rand_core::RngCore; use rand_core::RngCore;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use x448::{PublicKey, Secret, SharedSecret}; // from the `x448` crate use x448::{PublicKey, Secret, SharedSecret};
/// Errors for crypto operations /// Errors for crypto operations
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,5 +1,6 @@
use crate::communities::interactables::category::Category; use crate::communities::interactables::category::Category;
use crate::communities::interactables::registry; use crate::communities::interactables::registry;
use crate::communities::perms::permission::Permission;
use crate::communities::{ use crate::communities::{
community_connection::CommunityConnection, interactables::interactable::Interactable, community_connection::CommunityConnection, interactables::interactable::Interactable,
}; };
@ -27,8 +28,8 @@ pub struct Community {
name: String, name: String,
owner_id: Uuid, owner_id: Uuid,
members: Vec<Uuid>, members: Vec<Uuid>,
permissions: HashMap<Uuid, Vec<String>>, permissions: HashMap<Uuid, Vec<Permission>>,
roles: HashMap<String, Vec<String>>, roles: HashMap<String, Vec<Permission>>,
private_key: Secret, private_key: Secret,
public_key: PublicKey, public_key: PublicKey,
pub interactables: Arc<RwLock<Vec<Arc<Box<dyn Interactable>>>>>, pub interactables: Arc<RwLock<Vec<Arc<Box<dyn Interactable>>>>>,
@ -148,6 +149,13 @@ impl Community {
.await .await
.retain(|i| !Arc::ptr_eq(i, &interactable)); .retain(|i| !Arc::ptr_eq(i, &interactable));
} }
pub async fn broadcast_message(&self, message: &CommunicationValue) {
for (_, conns) in self.connections.read().await.clone().iter() {
for user in conns.iter() {
user.send_message(message).await;
}
}
}
pub async fn run_function( pub async fn run_function(
self: &mut Arc<Self>, self: &mut Arc<Self>,
user_id: Uuid, user_id: Uuid,
@ -240,7 +248,7 @@ pub async fn load(name: &String) -> Option<Arc<Community>> {
let user_data = file_util::load_file(&format!("communities/{}/", name), "users.json"); let user_data = file_util::load_file(&format!("communities/{}/", name), "users.json");
let user_json: JsonValue = json::parse(&user_data).unwrap(); let user_json: JsonValue = json::parse(&user_data).unwrap();
let mut users = Vec::new(); let mut users = Vec::new();
let mut permissions: HashMap<Uuid, Vec<String>> = HashMap::new(); let mut permissions: HashMap<Uuid, Vec<Permission>> = HashMap::new();
for user in user_json.entries() { for user in user_json.entries() {
let (str, json): (&str, &JsonValue) = user; let (str, json): (&str, &JsonValue) = user;
@ -258,7 +266,7 @@ pub async fn load(name: &String) -> Option<Arc<Community>> {
} }
let role_data = file_util::load_file(&format!("communities/{}/", name), "roles.json"); let role_data = file_util::load_file(&format!("communities/{}/", name), "roles.json");
let roles: HashMap<String, Vec<String>> = HashMap::new(); let roles: HashMap<String, Vec<Permission>> = HashMap::new();
if let Ok(_) = json::parse(&role_data) { if let Ok(_) = json::parse(&role_data) {
// Fill roles // Fill roles
} else { } else {

View file

@ -6,8 +6,10 @@ use async_trait::async_trait;
use json::JsonValue; use json::JsonValue;
use std::any::Any; use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use uuid::Uuid;
pub struct Category { pub struct Category {
id: Uuid,
name: String, name: String,
path: String, path: String,
community: Arc<Community>, community: Arc<Community>,
@ -16,6 +18,7 @@ pub struct Category {
impl Category { impl Category {
pub fn new() -> Category { pub fn new() -> Category {
Category { Category {
id: Uuid::new_v4(),
name: String::new(), name: String::new(),
path: String::new(), path: String::new(),
community: Arc::new(Community::new()), community: Arc::new(Community::new()),
@ -50,6 +53,9 @@ impl Category {
#[async_trait] #[async_trait]
impl Interactable for Category { impl Interactable for Category {
fn get_id(&self) -> &Uuid {
&self.id
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
@ -90,7 +96,7 @@ impl Interactable for Category {
} }
v v
} }
async fn run_function(&self, cv: CommunicationValue) -> CommunicationValue { async fn run_function(&self, _cv: CommunicationValue) -> CommunicationValue {
CommunicationValue::new(CommunicationType::error) CommunicationValue::new(CommunicationType::error)
} }
fn to_json(&self) -> JsonValue { fn to_json(&self) -> JsonValue {
@ -101,8 +107,16 @@ impl Interactable for Category {
} }
v v
} }
fn load(&mut self, community: Arc<Community>, path: String, name: String, _json: &JsonValue) { fn load(
&mut self,
community: Arc<Community>,
id: Uuid,
path: String,
name: String,
_json: &JsonValue,
) {
self.community = community; self.community = community;
self.id = id;
self.name = name; self.name = name;
self.path = path; self.path = path;
} }

View file

@ -3,6 +3,7 @@ use async_trait::async_trait;
use json::JsonValue; use json::JsonValue;
use std::any::Any; use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use uuid::Uuid;
pub type InteractableFactory = fn() -> Box<dyn Interactable>; pub type InteractableFactory = fn() -> Box<dyn Interactable>;
@ -20,6 +21,14 @@ pub trait Interactable: Send + Sync + Any {
fn set_community(&mut self, community: Arc<Community>); fn set_community(&mut self, community: Arc<Community>);
async fn run_function(&self, cv: CommunicationValue) -> CommunicationValue; async fn run_function(&self, cv: CommunicationValue) -> CommunicationValue;
fn get_data(&self) -> JsonValue; fn get_data(&self) -> JsonValue;
fn get_id(&self) -> &Uuid;
fn to_json(&self) -> JsonValue; fn to_json(&self) -> JsonValue;
fn load(&mut self, community: Arc<Community>, path: String, name: String, json: &JsonValue); fn load(
&mut self,
community: Arc<Community>,
id: Uuid,
path: String,
name: String,
json: &JsonValue,
);
} }

View file

@ -9,6 +9,7 @@ use once_cell::sync::Lazy;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use uuid::Uuid;
pub static INTERACTABLE_REGISTRY: Lazy<Arc<Mutex<HashMap<String, InteractableFactory>>>> = pub static INTERACTABLE_REGISTRY: Lazy<Arc<Mutex<HashMap<String, InteractableFactory>>>> =
Lazy::new(|| Arc::new(Mutex::new(HashMap::new()))); Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));
@ -45,6 +46,7 @@ pub async fn get_interactable(name: &str) -> Box<dyn Interactable> {
pub async fn save(interactable: &Arc<Box<dyn Interactable>>) { pub async fn save(interactable: &Arc<Box<dyn Interactable>>) {
let mut json_object: JsonValue = interactable.to_json().clone(); let mut json_object: JsonValue = interactable.to_json().clone();
json_object["codec"] = JsonValue::String(interactable.get_codec()); json_object["codec"] = JsonValue::String(interactable.get_codec());
json_object["id"] = JsonValue::String(interactable.get_id().to_string());
file_util::save_file( file_util::save_file(
&format!( &format!(
"communities/{}/interactables/{}", "communities/{}/interactables/{}",
@ -66,7 +68,8 @@ pub async fn load(
); );
let json_object: JsonValue = json::parse(&s).unwrap(); let json_object: JsonValue = json::parse(&s).unwrap();
let codec: String = json_object["codec"].as_str().unwrap().to_string(); let codec: String = json_object["codec"].as_str().unwrap().to_string();
let id: String = json_object["id"].as_str().unwrap().to_string();
let mut interactable = get_interactable(&codec).await; let mut interactable = get_interactable(&codec).await;
interactable.load(c, path, name, &json_object); interactable.load(c, Uuid::parse_str(&id).unwrap(), path, name, &json_object);
interactable interactable
} }

View file

@ -14,6 +14,7 @@ use std::sync::Arc;
use std::{any::Any, collections::HashMap}; use std::{any::Any, collections::HashMap};
use uuid::Uuid; use uuid::Uuid;
pub struct TextChat { pub struct TextChat {
id: Uuid,
name: String, name: String,
path: String, path: String,
community: Arc<Community>, community: Arc<Community>,
@ -21,6 +22,7 @@ pub struct TextChat {
impl TextChat { impl TextChat {
pub fn new() -> TextChat { pub fn new() -> TextChat {
TextChat { TextChat {
id: Uuid::new_v4(),
name: String::new(), name: String::new(),
path: String::new(), path: String::new(),
community: Arc::new(Community::new()), community: Arc::new(Community::new()),
@ -152,6 +154,9 @@ impl TextChat {
} }
#[async_trait] #[async_trait]
impl Interactable for TextChat { impl Interactable for TextChat {
fn get_id(&self) -> &Uuid {
&self.id
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
@ -242,8 +247,16 @@ impl Interactable for TextChat {
fn to_json(&self) -> JsonValue { fn to_json(&self) -> JsonValue {
JsonValue::new_object() JsonValue::new_object()
} }
fn load(&mut self, community: Arc<Community>, path: String, name: String, _: &JsonValue) { fn load(
&mut self,
community: Arc<Community>,
id: Uuid,
path: String,
name: String,
_json: &JsonValue,
) {
self.community = community; self.community = community;
self.id = id;
self.name = name; self.name = name;
self.path = path; self.path = path;
} }

View file

@ -37,6 +37,7 @@ pub struct CallUser {
} }
pub struct VoiceChat { pub struct VoiceChat {
id: Uuid,
name: String, name: String,
path: String, path: String,
community: Arc<Community>, community: Arc<Community>,
@ -45,6 +46,7 @@ pub struct VoiceChat {
impl VoiceChat { impl VoiceChat {
pub fn new() -> VoiceChat { pub fn new() -> VoiceChat {
VoiceChat { VoiceChat {
id: Uuid::new_v4(),
name: String::new(), name: String::new(),
path: String::new(), path: String::new(),
community: Arc::new(Community::new()), community: Arc::new(Community::new()),
@ -71,6 +73,9 @@ impl VoiceChat {
} }
#[async_trait] #[async_trait]
impl Interactable for VoiceChat { impl Interactable for VoiceChat {
fn get_id(&self) -> &Uuid {
&self.id
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
@ -102,25 +107,6 @@ impl Interactable for VoiceChat {
String::new() + &self.path + "/" + &self.name String::new() + &self.path + "/" + &self.name
} }
fn get_data(&self) -> JsonValue { fn get_data(&self) -> JsonValue {
/*
* "data": {
"active_users": {
"user_id": {
"state": "<call_status>",
"streaming": boolean
},
"user_id": {
"state": "<call_status>",
"streaming": boolean
},
"user_id": {
"state": "<call_status>",
"streaming": boolean
}
}
}
},
*/
let mut data = JsonValue::new_object(); let mut data = JsonValue::new_object();
let mut active_users = JsonValue::new_object(); let mut active_users = JsonValue::new_object();
for user in self.users.read().unwrap().iter() { for user in self.users.read().unwrap().iter() {
@ -188,8 +174,16 @@ impl Interactable for VoiceChat {
let v = JsonValue::new_object(); let v = JsonValue::new_object();
v v
} }
fn load(&mut self, community: Arc<Community>, path: String, name: String, _json: &JsonValue) { fn load(
&mut self,
community: Arc<Community>,
id: Uuid,
path: String,
name: String,
_json: &JsonValue,
) {
self.community = community; self.community = community;
self.id = id;
self.name = name; self.name = name;
self.path = path; self.path = path;
} }

View file

@ -8,3 +8,6 @@ pub mod interactables {
} }
pub mod community; pub mod community;
pub mod community_connection; pub mod community_connection;
pub mod perms {
pub mod permission;
}

View file

@ -0,0 +1,27 @@
use json::JsonValue;
use uuid::Uuid;
pub struct Permission {
pub id: Uuid,
pub name: String,
}
impl Permission {
pub fn new(id: Uuid, name: String) -> Self {
Permission { id, name }
}
pub fn to_json(&self) -> JsonValue {
json::object! {
"id" => self.id.to_string(),
"name" => self.name.clone()
}
}
pub fn from_json(json: JsonValue) -> Self {
Permission {
id: Uuid::parse_str(json["id"].as_str().unwrap()).unwrap(),
name: json["name"].as_str().unwrap().to_string(),
}
}
pub fn to_string(&self) -> String {
self.to_json().as_str().unwrap().to_string()
}
}

View file

@ -10,9 +10,9 @@ pub fn check_eula() -> bool {
return false; return false;
} }
if (file.contains("eula=false")) { if file.contains("eula=false") {
false false
} else if (file.contains("eula=true")) { } else if file.contains("eula=true") {
true true
} else { } else {
false false

View file

@ -138,7 +138,7 @@ pub fn render() {
.draw(|f| { .draw(|f| {
let sys = System::new_all(); let sys = System::new_all();
let size = f.size(); let size = f.area();
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Percentage(40), Constraint::Percentage(60)]) .constraints([Constraint::Percentage(40), Constraint::Percentage(60)])

View file

@ -2,4 +2,3 @@ pub mod contact;
pub mod user_community_util; pub mod user_community_util;
pub mod user_manager; pub mod user_manager;
pub mod user_profile; pub mod user_profile;
pub mod user_profile_full;

View file

@ -1,10 +1,9 @@
use crate::auth::auth_connector; use crate::auth::auth_connector;
use crate::users::user_profile::UserProfile; use crate::users::user_profile::UserProfile;
use crate::users::user_profile_full::UserProfileFull;
use crate::util::config_util::CONFIG; use crate::util::config_util::CONFIG;
use crate::util::file_util::{load_file, save_file}; use crate::util::file_util::{load_file, save_file};
use base64::{Engine as _, engine::general_purpose::STANDARD}; use base64::{Engine as _, engine::general_purpose::STANDARD};
use hex; use hex::{self};
use json::JsonValue; use json::JsonValue;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rand::Rng; use rand::Rng;
@ -19,7 +18,7 @@ use x448::{PublicKey, Secret};
static USERS: Lazy<Mutex<Vec<UserProfile>>> = Lazy::new(|| Mutex::new(Vec::new())); static USERS: Lazy<Mutex<Vec<UserProfile>>> = Lazy::new(|| Mutex::new(Vec::new()));
static UNIQUE: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false)); static UNIQUE: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
pub async fn create_user(username: &str) -> Option<UserProfileFull> { pub async fn create_user(username: &str) -> (Option<UserProfile>, Option<String>) {
let user_id = auth_connector::get_register().await.unwrap(); let user_id = auth_connector::get_register().await.unwrap();
let mut buf = [0u8; 56]; let mut buf = [0u8; 56];
let mut rng = OsRng; let mut rng = OsRng;
@ -45,11 +44,6 @@ pub async fn create_user(username: &str) -> Option<UserProfileFull> {
reset_token, reset_token,
); );
let up_full = UserProfileFull {
user_profile: up.clone(),
private_key: STANDARD.encode(&private_key.as_bytes()),
};
auth_connector::complete_register(&up, &CONFIG.lock().unwrap().get_iota_id().to_string()).await; auth_connector::complete_register(&up, &CONFIG.lock().unwrap().get_iota_id().to_string()).await;
save_file( save_file(
"", "",
@ -57,9 +51,9 @@ pub async fn create_user(username: &str) -> Option<UserProfileFull> {
&format!("{}::{}", user_id, STANDARD.encode(&private_key.as_bytes())), &format!("{}::{}", user_id, STANDARD.encode(&private_key.as_bytes())),
); );
USERS.lock().unwrap().push(up); USERS.lock().unwrap().push(up.clone());
save_users().ok(); save_users().ok();
Some(up_full) (Some(up), Some(STANDARD.encode(&private_key.as_bytes())))
} }
pub fn get_user(user_id: Uuid) -> Option<UserProfile> { pub fn get_user(user_id: Uuid) -> Option<UserProfile> {

View file

@ -1,8 +0,0 @@
use crate::users::user_profile::UserProfile;
// --- UserProfileFull ---
#[derive(Clone, Debug)]
pub struct UserProfileFull {
pub user_profile: UserProfile,
pub private_key: String,
}