- Add gitignore and prettier configuration files for project scaffolding - Implement room access control service with project member verification - Create user access key management with CRUD operations and activity logging - Add accordion UI component for frontend expandable sections - Implement room AI configuration with list, upsert, and delete operations - Add AI event types for agent join/leave/status change tracking - Create streaming AI processing services for mode and react patterns - Build room AI service with model detection and idempotency handling - Integrate chat service orchestration for AI message processing - Add typing indicators and stream cancellation for AI interactions - Implement mention parsing and context extraction for AI agents
87 lines
3.0 KiB
Rust
87 lines
3.0 KiB
Rust
use std::sync::Arc;
|
|
use uuid::Uuid;
|
|
use tokio::sync::broadcast;
|
|
|
|
use crate::error::RoomError;
|
|
use crate::types::NotificationEvent;
|
|
use super::{RoomConnectionManager, ProjectRoomEvent, BROADCAST_CAPACITY, MAX_CONNECTIONS_PER_USER};
|
|
|
|
impl RoomConnectionManager {
|
|
pub async fn subscribe_user(&self, user_id: Uuid) -> Result<broadcast::Receiver<Arc<ProjectRoomEvent>>, RoomError> {
|
|
let mut map = self.user_inner.write().await;
|
|
|
|
if let Some(_sender) = map.get(&user_id) {
|
|
drop(map);
|
|
let mut counts = self.user_subscriber_count.write().await;
|
|
*counts.entry(user_id).or_insert(0) += 1;
|
|
let map = self.user_inner.read().await;
|
|
if let Some(sender) = map.get(&user_id) {
|
|
return Ok(sender.subscribe());
|
|
}
|
|
return Err(RoomError::Internal("user channel disappeared".into()));
|
|
}
|
|
|
|
if map.len() >= MAX_CONNECTIONS_PER_USER {
|
|
return Err(RoomError::RateLimited(format!(
|
|
"User connection limit reached ({})",
|
|
MAX_CONNECTIONS_PER_USER
|
|
)));
|
|
}
|
|
|
|
let (tx, rx) = broadcast::channel(BROADCAST_CAPACITY);
|
|
map.insert(user_id, tx);
|
|
drop(map);
|
|
let mut counts = self.user_subscriber_count.write().await;
|
|
counts.insert(user_id, 1);
|
|
self.metrics.users_online.increment(1.0);
|
|
Ok(rx)
|
|
}
|
|
|
|
pub async fn unsubscribe_user(&self, user_id: Uuid) {
|
|
let mut counts = self.user_subscriber_count.write().await;
|
|
let count = counts.entry(user_id).or_insert(0);
|
|
if *count > 0 {
|
|
*count -= 1;
|
|
}
|
|
if *count == 0 {
|
|
self.metrics.users_online.decrement(1.0);
|
|
counts.remove(&user_id);
|
|
drop(counts);
|
|
let mut map = self.user_inner.write().await;
|
|
map.remove(&user_id);
|
|
}
|
|
}
|
|
|
|
pub async fn broadcast_to_user(&self, user_id: Uuid, event: ProjectRoomEvent) {
|
|
let map = self.user_inner.read().await;
|
|
if let Some(sender) = map.get(&user_id) {
|
|
let event = Arc::new(event);
|
|
if sender.send(event).is_err() {
|
|
self.metrics.broadcasts_dropped.increment(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn subscribe_user_notification(&self, user_id: Uuid) -> broadcast::Receiver<Arc<NotificationEvent>> {
|
|
let mut map = self.user_notification_inner.write().await;
|
|
if let Some(sender) = map.get(&user_id) {
|
|
return sender.subscribe();
|
|
}
|
|
let (tx, rx) = broadcast::channel(BROADCAST_CAPACITY);
|
|
map.insert(user_id, tx);
|
|
rx
|
|
}
|
|
|
|
pub async fn unsubscribe_user_notification(&self, user_id: Uuid) {
|
|
let mut map = self.user_notification_inner.write().await;
|
|
map.remove(&user_id);
|
|
}
|
|
|
|
pub async fn push_user_notification(&self, user_id: Uuid, event: Arc<NotificationEvent>) {
|
|
let map = self.user_notification_inner.read().await;
|
|
if let Some(sender) = map.get(&user_id) {
|
|
let _ = sender.send(event);
|
|
}
|
|
}
|
|
}
|