use super::types::{ServerExplorerItem, ServerTree}; use super::RepositoryContext; use crate::models::{category, channel, group, server}; use crate::utils::permissions::Permission; use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter, Set}; use std::sync::Arc; use uuid::Uuid; #[derive(Clone)] pub struct ServerRepository { pub context: Arc, } impl ServerRepository { pub async fn get_all(&self) -> Result, DbErr> { server::Entity::find().all(&self.context.db).await } pub async fn get_by_id(&self, id: uuid::Uuid) -> Result, DbErr> { server::Entity::find_by_id(id).one(&self.context.db).await } pub async fn update(&self, active: server::ActiveModel) -> Result { let server = active.update(&self.context.db).await?; self.context.events.emit("server_updated", server.clone()); Ok(server) } pub async fn create(&self, active: server::ActiveModel) -> Result { let server = active.insert(&self.context.db).await?; // Créer le groupe par défaut pour le serveur let default_group = group::ActiveModel { server_id: Set(server.id), name: Set("Membres".to_string()), permissions: Set(Permission::default_permissions() as i64), is_default: Set(true), ..Default::default() }; default_group.insert(&self.context.db).await?; self.context.events.emit("server_created", server.clone()); Ok(server) } pub async fn delete(&self, id: uuid::Uuid) -> Result { let res = server::Entity::delete_by_id(id) .exec(&self.context.db) .await?; self.context.events.emit("server_deleted", id); Ok(res.rows_affected > 0) } } // Helpers impl ServerRepository { pub async fn get_tree(&self, server_id: Uuid) -> Result { // 1. Récupération des catégories avec leurs channels let categories_with_channels = category::Entity::find() .filter(category::Column::ServerId.eq(server_id)) .find_with_related(channel::Entity) .all(&self.context.db) .await?; // 2. Récupération des channels orphelins (sans catégorie) let orphan_channels = channel::Entity::find() .filter(channel::Column::ServerId.eq(server_id)) .filter(channel::Column::CategoryId.is_null()) .all(&self.context.db) .await?; // 3. Transformation et tri des enfants let mut items: Vec = Vec::new(); for (cat, mut channels) in categories_with_channels { // On trie les channels internes (obligatoire car SQL ne garantit aucun ordre ici) channels.sort_by(|a, b| { a.position .cmp(&b.position) .then(a.created_at.cmp(&b.created_at)) }); items.push(ServerExplorerItem::Category(cat, channels)); } for chan in orphan_channels { items.push(ServerExplorerItem::Channel(chan)); } // 4. Tri final de la liste globale (Mélange catégories et orphelins) items.sort_by(|a, b| { let pos_cmp = a.position().cmp(&b.position()); if pos_cmp == std::cmp::Ordering::Equal { // Départage par date si position identique let date_a = match a { ServerExplorerItem::Category(c, _) => c.created_at, ServerExplorerItem::Channel(c) => c.created_at, }; let date_b = match b { ServerExplorerItem::Category(c, _) => c.created_at, ServerExplorerItem::Channel(c) => c.created_at, }; date_a.cmp(&date_b) } else { pos_cmp } }); Ok(ServerTree { items }) } }