From 96ffe27040ce5c686ae1c4a14cd637ae4aaaa3fd Mon Sep 17 00:00:00 2001 From: Nell Date: Tue, 28 Jul 2026 02:11:10 +0200 Subject: [PATCH] init --- src/repositories/category.rs | 7 +++++ src/routes/server/handlers.rs | 43 +++++++++++++++++++++++++- src/routes/server/mapper.rs | 57 ++++++++++++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/src/repositories/category.rs b/src/repositories/category.rs index f0f4f58..2bfbb27 100644 --- a/src/repositories/category.rs +++ b/src/repositories/category.rs @@ -16,6 +16,13 @@ impl CategoryRepository { .await?) } + pub async fn get_by_server(&self, server_id: Uuid) -> AnyResult> { + Ok(category::Entity::find() + .filter(category::Column::ServerId.eq(server_id)) + .all(&self.context.db) + .await?) + } + pub async fn get_all(&self) -> AnyResult> { Ok(category::Entity::find().all(&self.context.db).await?) } diff --git a/src/routes/server/handlers.rs b/src/routes/server/handlers.rs index 185f588..72396e8 100644 --- a/src/routes/server/handlers.rs +++ b/src/routes/server/handlers.rs @@ -1,15 +1,16 @@ use crate::core::state::AppState; use crate::http::context::Superuser; use crate::http::error::HTTPError; +use crate::repositories::types::{ChannelFilter, ServerTree}; use crate::routes::server::dto::{ CreateServerRequest, ServerResponse, ServerRolePermissionResponse, ServerUserPermissionResponse, SetServerPermissionRequest, UpdateServerRequest, }; use crate::routes::server::mapper; use axum::{ + Json, extract::{Path, State}, http::StatusCode, - Json, }; use uuid::Uuid; @@ -362,3 +363,43 @@ pub async fn remove_role_permission( Ok(StatusCode::NO_CONTENT) } + +#[utoipa::path( + get, + path = "/servers/{id}/tree", + responses( + (status = 200, description = "Arborescence du serveur récupérée avec succès"), + (status = 404, description = "Serveur introuvable"), + (status = 500, description = "Erreur interne du serveur") + ), + params( + ("id" = Uuid, Path, description = "ID du serveur") + ), + tag = "Servers" +)] +pub async fn get_server_tree( + State(state): State, + Path(id): Path, +) -> Result, HTTPError> { + // Vérifier l'existence du serveur + state + .repositories + .server + .get_by_id(id) + .await? + .ok_or(HTTPError::NotFound)?; + + // Exécution parallèle des 3 requêtes via leurs repositories respectifs + let (orders, channels, categories) = tokio::try_join!( + state.repositories.server_item_order.get_by_server(id), + state.repositories.channel.filter(ChannelFilter { + server_id: Some(id) + }), + state.repositories.category.get_by_server(id), + )?; + + // Assemblage du ServerTree via le mapper + let tree = mapper::build_server_tree(orders, channels, categories); + + Ok(Json(tree)) +} diff --git a/src/routes/server/mapper.rs b/src/routes/server/mapper.rs index b5c33a6..01f00a5 100644 --- a/src/routes/server/mapper.rs +++ b/src/routes/server/mapper.rs @@ -1,9 +1,13 @@ -use crate::models::{server, server_role_permission, server_user_permission}; +use crate::models::{ + category, channel, server, server_item_order, server_role_permission, server_user_permission, +}; +use crate::repositories::types::{ServerExplorerItem, ServerTree}; use crate::routes::server::dto::{ CreateServerRequest, ServerResponse, ServerRolePermissionResponse, ServerUserPermissionResponse, UpdateServerRequest, }; use sea_orm::Set; +use std::collections::HashMap; use uuid::Uuid; pub fn server_model_to_server_response(model: server::Model) -> ServerResponse { @@ -57,3 +61,54 @@ pub fn server_role_permission_to_response( permissions: model.permissions as u64, } } + +pub fn build_server_tree( + orders: Vec, + channels: Vec, + categories: Vec, +) -> ServerTree { + // Map d'accès rapide : resource_id -> order_key + let order_map: HashMap = orders + .into_iter() + .map(|o| (o.resource_id, o.order_key)) + .collect(); + + // Regrouper les canaux par catégorie ou orphelins + let mut category_channels: HashMap> = HashMap::new(); + let mut orphan_channels: Vec = Vec::new(); + + for chan in channels { + if let Some(cat_id) = chan.category_id { + category_channels.entry(cat_id).or_default().push(chan); + } else { + orphan_channels.push(chan); + } + } + + // Trier les canaux dans chaque catégorie par leur order_key + for chans in category_channels.values_mut() { + chans.sort_by_key(|c| order_map.get(&c.id).copied().unwrap_or(i64::MAX)); + } + + // Préparer les éléments racine (catégories & canaux orphelins) avec leur order_key + let mut root_items: Vec<(ServerExplorerItem, i64)> = Vec::new(); + + for cat in categories { + let cat_id = cat.id; + let order_key = order_map.get(&cat_id).copied().unwrap_or(i64::MAX); + let chans = category_channels.remove(&cat_id).unwrap_or_default(); + root_items.push((ServerExplorerItem::Category(cat, chans), order_key)); + } + + for chan in orphan_channels { + let order_key = order_map.get(&chan.id).copied().unwrap_or(i64::MAX); + root_items.push((ServerExplorerItem::Channel(chan), order_key)); + } + + // Tri du niveau racine par order_key + root_items.sort_by_key(|(_, order_key)| *order_key); + + let items = root_items.into_iter().map(|(item, _)| item).collect(); + + ServerTree { items } +}