veza/veza-chat-server/src/core/room.rs
2025-12-03 20:33:26 +01:00

239 lines
6.3 KiB
Rust

//! Room Management pour Chat Production
//!
//! Gestion des salles de chat avec permissions Discord-like
//! et optimisations pour haute performance.
use std::sync::Arc;
use dashmap::DashMap;
use tokio::sync::RwLock;
use uuid::Uuid;
use serde::{Serialize, Deserialize};
use chrono::{DateTime, Utc};
use super::message::*;
use super::user::*;
/// Salle de chat optimisée
#[derive(Debug)]
pub struct Room {
/// Identifiant de la salle
pub id: String,
/// Nom de la salle
pub name: String,
/// Membres connectés
pub members: Arc<DashMap<Uuid, RoomMember>>,
/// Configuration de la salle
pub settings: RoomSettings,
/// Buffer circulaire pour messages récents
pub message_buffer: Arc<RwLock<MessageBuffer>>,
/// Tracker de présence
pub presence_tracker: Arc<PresenceTracker>,
}
/// Membre d'une salle
#[derive(Debug, Clone)]
pub struct RoomMember {
pub connection_id: Uuid,
pub user_id: i64,
pub joined_at: DateTime<Utc>,
pub permissions: RoomPermissions,
pub status: PresenceStatus,
}
/// Permissions dans une salle (Discord-like)
#[derive(Debug, Clone, PartialEq)]
pub struct RoomPermissions {
// Permissions générales
pub view_channel: bool,
pub send_messages: bool,
pub embed_links: bool,
pub attach_files: bool,
pub read_message_history: bool,
pub mention_everyone: bool,
pub use_external_emojis: bool,
pub add_reactions: bool,
// Permissions modération
pub manage_messages: bool,
pub manage_channel: bool,
pub kick_members: bool,
pub ban_members: bool,
// Permissions voix
pub connect_voice: bool,
pub speak: bool,
pub mute_members: bool,
pub move_members: bool,
}
impl Default for RoomPermissions {
fn default() -> Self {
Self {
view_channel: true,
send_messages: true,
embed_links: true,
attach_files: true,
read_message_history: true,
mention_everyone: false,
use_external_emojis: true,
add_reactions: true,
manage_messages: false,
manage_channel: false,
kick_members: false,
ban_members: false,
connect_voice: true,
speak: true,
mute_members: false,
move_members: false,
}
}
}
/// Configuration d'une salle
#[derive(Debug, Clone)]
pub struct RoomSettings {
pub is_public: bool,
pub max_members: Option<usize>,
pub rate_limit: Option<u32>,
pub enable_file_upload: bool,
pub enable_voice: bool,
pub channel_type: ChannelType,
pub topic: Option<String>,
pub slow_mode: Option<u32>, // secondes entre messages
}
/// Type de channel Discord-like
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ChannelType {
Text,
Voice,
Announcement,
Stage,
Forum,
Category,
}
impl Default for RoomSettings {
fn default() -> Self {
Self {
is_public: true,
max_members: None,
rate_limit: Some(10),
enable_file_upload: true,
enable_voice: false,
channel_type: ChannelType::Text,
topic: None,
slow_mode: None,
}
}
}
/// Buffer circulaire pour messages récents
#[derive(Debug)]
pub struct MessageBuffer {
messages: Vec<StoredMessage>,
capacity: usize,
index: usize,
}
impl MessageBuffer {
pub fn new(capacity: usize) -> Self {
Self {
messages: Vec::with_capacity(capacity),
capacity,
index: 0,
}
}
pub fn add_message(&mut self, message: StoredMessage) {
if self.messages.len() < self.capacity {
self.messages.push(message);
} else {
self.messages[self.index] = message;
self.index = (self.index + 1) % self.capacity;
}
}
pub fn get_recent_messages(&self, limit: usize) -> Vec<&StoredMessage> {
let len = self.messages.len().min(limit);
if self.messages.len() < self.capacity {
self.messages.iter().rev().take(len).collect()
} else {
let mut result = Vec::with_capacity(len);
for i in 0..len {
let idx = (self.index + self.capacity - 1 - i) % self.capacity;
result.push(&self.messages[idx]);
}
result
}
}
}
impl Room {
pub fn new(id: String, name: String, settings: RoomSettings) -> Self {
Self {
id,
name,
members: Arc::new(DashMap::new()),
settings,
message_buffer: Arc::new(RwLock::new(MessageBuffer::new(1000))),
presence_tracker: Arc::new(PresenceTracker::new()),
}
}
/// Ajoute un membre à la salle
pub async fn add_member(
&self,
connection_id: Uuid,
user_id: i64,
permissions: RoomPermissions,
) -> Result<(), &'static str> {
if let Some(max) = self.settings.max_members {
if self.members.len() >= max {
return Err("Room is full");
}
}
let member = RoomMember {
connection_id,
user_id,
joined_at: Utc::now(),
permissions,
status: PresenceStatus::Online,
};
self.members.insert(connection_id, member);
self.presence_tracker.update_status(user_id, PresenceStatus::Online);
Ok(())
}
/// Retire un membre de la salle
pub async fn remove_member(&self, connection_id: Uuid) {
if let Some((_, member)) = self.members.remove(&connection_id) {
// Vérifier si c'était la dernière connexion de cet utilisateur
let user_still_connected = self.members.iter()
.any(|entry| entry.value().user_id == member.user_id);
if !user_still_connected {
self.presence_tracker.update_status(member.user_id, PresenceStatus::Invisible);
}
}
}
/// Vérifie les permissions d'un membre
pub fn check_permission(
&self,
connection_id: Uuid,
permission: fn(&RoomPermissions) -> bool,
) -> bool {
self.members.get(&connection_id)
.map(|member| permission(&member.permissions))
.unwrap_or(false)
}
}