use crate::core::state::AppState; use crate::domain::dto::channel::{ ChannelPermissionsResponse, ChannelQueryParams, ChannelResponse, ChannelRolePermissionResponse, ChannelUserPermissionResponse, CreateChannelRequest, ReadStateResponse, SetChannelPermissionRequest, SetReadStateRequest, UpdateChannelRequest, }; use crate::http::context::{CurrentUser, Superuser}; use crate::http::error::HTTPError; use crate::models::{channel, channel_user}; use crate::routes::channel::mapper; use axum::{ Json, extract::{Path, Query, State}, http::StatusCode, }; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use uuid::Uuid; async fn require_channel_member( state: &AppState, channel_id: Uuid, user_id: Uuid, ) -> Result<(), HTTPError> { let channel = state .repositories .channel .get_by_id(channel_id) .await? .ok_or(HTTPError::NotFound)?; if channel.channel_type == channel::ChannelType::DM && channel_user::Entity::find() .filter(channel_user::Column::ChannelId.eq(channel_id)) .filter(channel_user::Column::UserId.eq(user_id)) .one(&state.db) .await? .is_none() { return Err(HTTPError::Forbidden); } Ok(()) } /// Liste tous les channels #[utoipa::path( get, path = "/channels", responses( (status = 200, description = "Liste des channels récupérée avec succès", body = [ChannelResponse]), (status = 500, description = "Erreur interne du serveur") ), params( ChannelQueryParams ), tag = "Channels" )] pub async fn get_all( State(state): State, Query(filters): Query, ) -> Result>, HTTPError> { let params = mapper::query_params_to_channel_filter(filters); let channels = state.repositories.channel.filter(params).await?; Ok(Json( channels .into_iter() .map(mapper::channel_model_to_channel_response) .collect(), )) } #[utoipa::path( get, path = "/channels/{channel_id}/read-state", params(("channel_id" = Uuid, Path, description = "ID du canal")), responses((status = 200, body = ReadStateResponse), (status = 404, description = "Canal non trouvé")), tag = "Channels", security(("bearerAuth" = [])) )] pub async fn get_read_state( user: CurrentUser, State(state): State, Path(channel_id): Path, ) -> Result, HTTPError> { require_channel_member(&state, channel_id, user.id).await?; let read_state = state .repositories .read_state .get(channel_id, user.id) .await?; let unread_count = state .repositories .read_state .unread_counts(&[channel_id], user.id) .await? .get(&channel_id) .copied() .unwrap_or(0); Ok(Json(ReadStateResponse { channel_id, last_read_message_id: read_state .as_ref() .and_then(|value| value.last_read_message_id), updated_at: read_state.map(|value| value.updated_at), unread_count, })) } #[utoipa::path( put, path = "/channels/{channel_id}/read-state", request_body = SetReadStateRequest, params(("channel_id" = Uuid, Path, description = "ID du canal")), responses((status = 200, body = ReadStateResponse), (status = 400, description = "Message invalide"), (status = 404, description = "Canal non trouvé")), tag = "Channels", security(("bearerAuth" = [])) )] pub async fn set_read_state( user: CurrentUser, State(state): State, Path(channel_id): Path, Json(payload): Json, ) -> Result, HTTPError> { require_channel_member(&state, channel_id, user.id).await?; if let Some(message_id) = payload.last_read_message_id { let message = state .repositories .message .get_by_id(message_id) .await? .ok_or(HTTPError::BadRequest("Message not found".to_string()))?; if message.channel_id != channel_id { return Err(HTTPError::BadRequest( "Message does not belong to this channel".to_string(), )); } } let read_state = state .repositories .read_state .set(channel_id, user.id, payload.last_read_message_id) .await?; let unread_count = state .repositories .read_state .unread_counts(&[channel_id], user.id) .await? .get(&channel_id) .copied() .unwrap_or(0); Ok(Json(ReadStateResponse { channel_id, last_read_message_id: read_state.last_read_message_id, updated_at: Some(read_state.updated_at), unread_count, })) } /// Récupère un channel par son ID #[utoipa::path( get, path = "/channels/{id}", responses( (status = 200, description = "Channel trouvé", body = ChannelResponse), (status = 404, description = "Channel non trouvé"), (status = 500, description = "Erreur interne du serveur") ), params( ("id" = Uuid, Path, description = "ID du channel") ), tag = "Channels" )] pub async fn get_by_id( State(state): State, Path(id): Path, ) -> Result, HTTPError> { let channel = state .repositories .channel .get_by_id(id) .await? .ok_or(HTTPError::NotFound)?; Ok(Json(mapper::channel_model_to_channel_response(channel))) } /// Liste les permissions directes configurées pour un canal. #[utoipa::path( get, path = "/channels/{channel_id}/permissions", params(("channel_id" = Uuid, Path, description = "ID du canal")), responses((status = 200, body = ChannelPermissionsResponse), (status = 404, description = "Canal non trouvé")), tag = "Channel Permissions" )] pub async fn list_permissions( State(state): State, Path(channel_id): Path, ) -> Result, HTTPError> { state .repositories .channel .get_by_id(channel_id) .await? .ok_or(HTTPError::NotFound)?; let (users, roles) = tokio::try_join!( state.repositories.channel.list_user_permissions(channel_id), state.repositories.channel.list_role_permissions(channel_id), )?; Ok(Json(mapper::channel_permissions_to_response(users, roles))) } /// Crée un nouveau channel #[utoipa::path( post, path = "/channels", request_body = CreateChannelRequest, responses( (status = 201, description = "Channel créé avec succès", body = ChannelResponse), (status = 400, description = "Données invalides (Serveur ou Catégorie non trouvée)"), (status = 500, description = "Erreur interne du serveur") ), tag = "Channels", security( ("bearerAuth" = []) ) )] pub async fn create( _admin: Superuser, State(state): State, Json(payload): Json, ) -> Result<(StatusCode, Json), HTTPError> { // Vérifier que le serveur existe si fourni if let Some(server_id) = payload.server_id { state .repositories .server .get_by_id(server_id) .await? .ok_or(HTTPError::BadRequest("Server not found".to_string()))?; } // Vérifier que la catégorie existe si fournie if let Some(category_id) = payload.category_id { state .repositories .category .get_by_id(category_id) .await? .ok_or(HTTPError::BadRequest("Category not found".to_string()))?; } let channel = state.services.channel.create_channel(payload).await?; Ok(( StatusCode::CREATED, Json(mapper::channel_model_to_channel_response(channel)), )) } /// Met à jour un channel existant #[utoipa::path( put, path = "/channels/{id}", request_body = UpdateChannelRequest, responses( (status = 200, description = "Channel mis à jour avec succès", body = ChannelResponse), (status = 404, description = "Channel non trouvé"), (status = 400, description = "Données invalides (Serveur ou Catégorie non trouvée)"), (status = 500, description = "Erreur interne du serveur") ), params( ("id" = Uuid, Path, description = "ID du channel") ), tag = "Channels", security( ("bearerAuth" = []) ) )] pub async fn update( _admin: Superuser, State(state): State, Path(id): Path, Json(payload): Json, ) -> Result, HTTPError> { // Vérifier l'existence state .repositories .channel .get_by_id(id) .await? .ok_or(HTTPError::NotFound)?; // Vérifier que le serveur existe si fourni if let Some(server_id) = payload.server_id { state .repositories .server .get_by_id(server_id) .await? .ok_or(HTTPError::BadRequest("Server not found".to_string()))?; } // Vérifier que la catégorie existe si fournie if let Some(category_id) = payload.category_id { state .repositories .category .get_by_id(category_id) .await? .ok_or(HTTPError::BadRequest("Category not found".to_string()))?; } let channel = state.services.channel.update_channel(id, payload).await?; Ok(Json(mapper::channel_model_to_channel_response(channel))) } /// Supprime un channel #[utoipa::path( delete, path = "/channels/{id}", responses( (status = 204, description = "Channel supprimé avec succès"), (status = 404, description = "Channel non trouvé"), (status = 500, description = "Erreur interne du serveur") ), params( ("id" = Uuid, Path, description = "ID du channel") ), tag = "Channels", security( ("bearerAuth" = []) ) )] pub async fn delete( _admin: Superuser, State(state): State, Path(id): Path, ) -> Result { if state.services.channel.delete_channel(id).await? { Ok(StatusCode::NO_CONTENT) } else { Err(HTTPError::NotFound) } } /// Récupère les permissions directes d'un utilisateur dans un canal. #[utoipa::path( get, path = "/channels/{channel_id}/permissions/users/{user_id}", params( ("channel_id" = Uuid, Path, description = "ID du canal"), ("user_id" = Uuid, Path, description = "ID de l'utilisateur") ), responses( (status = 200, body = ChannelUserPermissionResponse), (status = 404, description = "Permission introuvable"), (status = 500, description = "Erreur interne du serveur") ), tag = "Channel Permissions" )] pub async fn get_user_permission( State(state): State, Path((channel_id, user_id)): Path<(Uuid, Uuid)>, ) -> Result, HTTPError> { let permission = state .repositories .channel .get_user_permission(channel_id, user_id) .await? .ok_or(HTTPError::NotFound)?; Ok(Json(mapper::channel_user_permission_to_response( permission, ))) } /// Définit ou remplace les permissions directes d'un utilisateur dans un canal. #[utoipa::path( put, path = "/channels/{channel_id}/permissions/users/{user_id}", request_body = SetChannelPermissionRequest, params( ("channel_id" = Uuid, Path, description = "ID du canal"), ("user_id" = Uuid, Path, description = "ID de l'utilisateur") ), responses( (status = 200, body = ChannelUserPermissionResponse), (status = 500, description = "Erreur interne du serveur") ), tag = "Channel Permissions" )] pub async fn set_user_permission( State(state): State, Path((channel_id, user_id)): Path<(Uuid, Uuid)>, Json(payload): Json, ) -> Result, HTTPError> { state .services .channel .set_user_permission(channel_id, user_id, payload.permissions) .await?; let permission = state .repositories .channel .get_user_permission(channel_id, user_id) .await? .ok_or(HTTPError::NotFound)?; Ok(Json(mapper::channel_user_permission_to_response( permission, ))) } /// Supprime les permissions directes d'un utilisateur dans un canal. #[utoipa::path( delete, path = "/channels/{channel_id}/permissions/users/{user_id}", params( ("channel_id" = Uuid, Path, description = "ID du canal"), ("user_id" = Uuid, Path, description = "ID de l'utilisateur") ), responses( (status = 204, description = "Permission supprimée"), (status = 404, description = "Permission introuvable"), (status = 500, description = "Erreur interne du serveur") ), tag = "Channel Permissions" )] pub async fn remove_user_permission( State(state): State, Path((channel_id, user_id)): Path<(Uuid, Uuid)>, ) -> Result { if state .repositories .channel .get_user_permission(channel_id, user_id) .await? .is_none() { return Err(HTTPError::NotFound); } state .services .channel .remove_user_permission(channel_id, user_id) .await?; Ok(StatusCode::NO_CONTENT) } /// Récupère les permissions d'un rôle dans un canal. #[utoipa::path( get, path = "/channels/{channel_id}/permissions/roles/{role_id}", params( ("channel_id" = Uuid, Path, description = "ID du canal"), ("role_id" = Uuid, Path, description = "ID du rôle") ), responses( (status = 200, body = ChannelRolePermissionResponse), (status = 404, description = "Permission introuvable"), (status = 500, description = "Erreur interne du serveur") ), tag = "Channel Permissions" )] pub async fn get_role_permission( State(state): State, Path((channel_id, role_id)): Path<(Uuid, Uuid)>, ) -> Result, HTTPError> { let permission = state .repositories .channel .get_role_permission(channel_id, role_id) .await? .ok_or(HTTPError::NotFound)?; Ok(Json(mapper::channel_role_permission_to_response( permission, ))) } /// Définit ou remplace les permissions d'un rôle dans un canal. #[utoipa::path( put, path = "/channels/{channel_id}/permissions/roles/{role_id}", request_body = SetChannelPermissionRequest, params( ("channel_id" = Uuid, Path, description = "ID du canal"), ("role_id" = Uuid, Path, description = "ID du rôle") ), responses( (status = 200, body = ChannelRolePermissionResponse), (status = 500, description = "Erreur interne du serveur") ), tag = "Channel Permissions" )] pub async fn set_role_permission( State(state): State, Path((channel_id, role_id)): Path<(Uuid, Uuid)>, Json(payload): Json, ) -> Result, HTTPError> { state .services .channel .set_role_permission(channel_id, role_id, payload.permissions) .await?; let permission = state .repositories .channel .get_role_permission(channel_id, role_id) .await? .ok_or(HTTPError::NotFound)?; Ok(Json(mapper::channel_role_permission_to_response( permission, ))) } /// Supprime les permissions d'un rôle dans un canal. #[utoipa::path( delete, path = "/channels/{channel_id}/permissions/roles/{role_id}", params( ("channel_id" = Uuid, Path, description = "ID du canal"), ("role_id" = Uuid, Path, description = "ID du rôle") ), responses( (status = 204, description = "Permission supprimée"), (status = 404, description = "Permission introuvable"), (status = 500, description = "Erreur interne du serveur") ), tag = "Channel Permissions" )] pub async fn remove_role_permission( State(state): State, Path((channel_id, role_id)): Path<(Uuid, Uuid)>, ) -> Result { if state .repositories .channel .get_role_permission(channel_id, role_id) .await? .is_none() { return Err(HTTPError::NotFound); } state .services .channel .remove_role_permission(channel_id, role_id) .await?; Ok(StatusCode::NO_CONTENT) }