use actix_web::{HttpRequest, HttpResponse, web}; use channel::ChannelBus; use channel::http::{WsHandler, WsInMessage}; use serde::Deserialize; use uuid::Uuid; use super::rest::{channel_err, created_json, extract_user, ok_json}; use crate::error::ApiError; #[derive(Debug, Deserialize, utoipa::ToSchema)] pub struct RoomCreateRequest { pub workspace: Uuid, pub room_name: String, pub public: bool, pub category: Option, } #[derive(Debug, Deserialize, utoipa::ToSchema)] pub struct RoomUpdateRequest { pub room_name: Option, pub public: Option, pub category: Option, } #[derive(Debug, Deserialize, utoipa::ToSchema)] pub struct AccessRequest { pub user: Uuid, } #[derive(Debug, Deserialize, utoipa::ToSchema)] pub struct CategoryCreateRequest { pub name: String, pub position: Option, } #[derive(Debug, Deserialize, utoipa::ToSchema)] pub struct CategoryUpdateRequest { pub name: Option, pub position: Option, } #[utoipa::path( get, path = "/api/v1/ws/rooms", responses((status = 200, description = "List of rooms")), tag = "channel", )] pub async fn list_rooms( req: HttpRequest, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let rooms = bus.list_user_rooms(user_id) .await .map_err(channel_err)?; let categories = bus.list_user_categories(user_id) .await .map_err(channel_err)?; let workspace_id = if let Some(r) = rooms.first() { Some(r.workspace_id) } else { bus.first_workspace_id(user_id).await.unwrap_or(None) }; Ok(HttpResponse::Ok().json(serde_json::json!({ "rooms": rooms, "categories": categories, "workspace_id": workspace_id, }))) } #[utoipa::path( post, path = "/api/v1/ws/rooms/{room_id}/subscribe", responses((status = 204, description = "Subscribed, user room cache refreshed")), tag = "channel", )] pub async fn subscribe( req: HttpRequest, room_id: web::Path, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::Subscribe { room: room_id.into_inner(), }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(ok_json(result)) } #[utoipa::path( delete, path = "/api/v1/ws/rooms/{room_id}/subscribe", responses((status = 204, description = "Unsubscribed")), tag = "channel", )] pub async fn unsubscribe( req: HttpRequest, room_id: web::Path, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::Unsubscribe { room: room_id.into_inner(), }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(ok_json(result)) } #[utoipa::path( get, path = "/api/v1/ws/rooms/{room_id}", responses((status = 200, description = "Room info")), tag = "channel", )] pub async fn room_get( req: HttpRequest, room_id: web::Path, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::RoomGet { room: room_id.into_inner(), }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(ok_json(result)) } #[utoipa::path( post, path = "/api/v1/ws/rooms", request_body = RoomCreateRequest, responses((status = 201, description = "Room created")), tag = "channel", )] pub async fn room_create( req: HttpRequest, body: web::Json, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::RoomCreate { workspace: body.workspace, room_name: body.room_name.clone(), public: body.public, category: body.category, }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(created_json(result)) } #[utoipa::path( patch, path = "/api/v1/ws/rooms/{room_id}", request_body = RoomUpdateRequest, responses((status = 200, description = "Room updated")), tag = "channel", )] pub async fn room_update( req: HttpRequest, room_id: web::Path, body: web::Json, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::RoomUpdate { room: room_id.into_inner(), room_name: body.room_name.clone(), public: body.public, category: body.category, }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(ok_json(result)) } #[utoipa::path( delete, path = "/api/v1/ws/rooms/{room_id}", responses((status = 204, description = "Room deleted")), tag = "channel", )] pub async fn room_delete( req: HttpRequest, room_id: web::Path, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::RoomDelete { room: room_id.into_inner(), }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(ok_json(result)) } #[utoipa::path( post, path = "/api/v1/ws/rooms/{room_id}/members", request_body = AccessRequest, responses((status = 204, description = "Access granted")), tag = "channel", )] pub async fn access_grant( req: HttpRequest, room_id: web::Path, body: web::Json, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::AccessGrant { room: room_id.into_inner(), user: body.user, }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(ok_json(result)) } #[utoipa::path( delete, path = "/api/v1/ws/rooms/{room_id}/members/{user_id}", responses((status = 204, description = "Access revoked")), tag = "channel", )] pub async fn access_revoke( req: HttpRequest, path: web::Path<(Uuid, Uuid)>, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let (room, target_user) = path.into_inner(); let msg = WsInMessage::AccessRevoke { room, user: target_user, }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(ok_json(result)) } #[utoipa::path( post, path = "/api/v1/ws/workspaces/{workspace_id}/categories", request_body = CategoryCreateRequest, responses((status = 201, description = "Category created")), tag = "channel", )] pub async fn category_create( req: HttpRequest, workspace_id: web::Path, body: web::Json, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::CategoryCreate { workspace: workspace_id.into_inner(), name: body.name.clone(), position: body.position, }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(created_json(result)) } #[utoipa::path( patch, path = "/api/v1/ws/categories/{category_id}", request_body = CategoryUpdateRequest, responses((status = 200, description = "Category updated")), tag = "channel", )] pub async fn category_update( req: HttpRequest, category_id: web::Path, body: web::Json, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::CategoryUpdate { id: category_id.into_inner(), name: body.name.clone(), position: body.position, }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(ok_json(result)) } #[utoipa::path( delete, path = "/api/v1/ws/categories/{category_id}", responses((status = 204, description = "Category deleted")), tag = "channel", )] pub async fn category_delete( req: HttpRequest, category_id: web::Path, bus: web::Data, ) -> Result { let user_id = extract_user(&req)?; let msg = WsInMessage::CategoryDelete { id: category_id.into_inner(), }; let result = WsHandler::handle(&bus, user_id, msg) .await .map_err(channel_err)?; Ok(ok_json(result)) }