use axum::{response::Response, Router, routing::{post, patch, delete}}; use serde::Deserialize; use crate::{ public::docs::{EndpointDocumentation, EndpointMethod}, types::{ extract::{AuthorizedUser, Check, CheckResult, Database, Json}, http::ResponseCode, chat::ChatRoom, user::User, }, }; pub const CHAT_LIST: EndpointDocumentation = EndpointDocumentation { uri: "/api/chat/list", method: EndpointMethod::Post, description: "Returns the rooms you are in", body: None, responses: &[ (201, "Returns rooms in a list"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to retrieve rooms"), ], cookie: Some("auth"), }; async fn list ( AuthorizedUser(user): AuthorizedUser, Database(db): Database ) -> Response { let Ok(rooms) = ChatRoom::from_user_id(&db, user.user_id) else { return ResponseCode::InternalServerError.text("Failed to retrieve rooms") }; let Ok(json) = serde_json::to_string(&rooms) else { return ResponseCode::InternalServerError.text("Failed to retrieve rooms") }; ResponseCode::Success.json(&json) } pub const CHAT_CREATE: EndpointDocumentation = EndpointDocumentation { uri: "/api/chat/create", method: EndpointMethod::Post, description: "Creates a new room", body: Some( r#" { "name" : "Funny memes" } "#, ), responses: &[ (201, "Successfully created room"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to create room"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct RoomCreateRequest { name: String, } impl Check for RoomCreateRequest { fn check(&self) -> CheckResult { Self::assert_length( &self.name, 1, 255, "Room names must be between 1-255 characters long", )?; Ok(()) } } async fn create ( AuthorizedUser(user): AuthorizedUser, Database(db): Database, Json(body): Json, ) -> Response { let Ok(post) = ChatRoom::new(&db, vec![user.user_id], body.name) else { return ResponseCode::InternalServerError.text("Failed to create room") }; let Ok(json) = serde_json::to_string(&post) else { return ResponseCode::InternalServerError.text("Failed to create room") }; ResponseCode::Created.json(&json) } pub const CHAT_ADD: EndpointDocumentation = EndpointDocumentation { uri: "/api/chat/add", method: EndpointMethod::Patch, description: "Adds a user to a room", body: Some( r#" { "room_id": 69, "email" : "joebide@house.gov" } "#, ), responses: &[ (201, "Successfully added user"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to add user"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct AddUserRequest { room_id: u64, email: String, } impl Check for AddUserRequest { fn check(&self) -> CheckResult { Ok(()) } } async fn add ( AuthorizedUser(user): AuthorizedUser, Database(db): Database, Json(body): Json, ) -> Response { let Ok(to_add) = User::from_email(&db, &body.email) else { return ResponseCode::BadRequest.text("User does not exist") }; let Ok(room) = ChatRoom::from_user_and_room_id(&db, user.user_id, body.room_id) else { return ResponseCode::BadRequest.text("Room doesnt exist or you are not in it") }; let Ok(success) = room.add_user(&db, to_add.user_id) else { return ResponseCode::InternalServerError.text("Failed to add user") }; if !success { return ResponseCode::BadRequest.text("User is already in the room") } ResponseCode::Success.text("Successfully added user") } pub const CHAT_LEAVE: EndpointDocumentation = EndpointDocumentation { uri: "/api/chat/leave", method: EndpointMethod::Delete, description: "Leaves a room", body: Some( r#" { "room_id": 69 } "#, ), responses: &[ (201, "Successfully left room"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to leave a room"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct LeaveRoomRequest { room_id: u64, } impl Check for LeaveRoomRequest { fn check(&self) -> CheckResult { Ok(()) } } async fn leave ( AuthorizedUser(user): AuthorizedUser, Database(db): Database, Json(body): Json, ) -> Response { let Ok(room) = ChatRoom::from_user_and_room_id(&db, user.user_id, body.room_id) else { return ResponseCode::BadRequest.text("Room doesnt exist or you are not in it") }; let Ok(success) = room.remove_user(&db, user.user_id) else { return ResponseCode::InternalServerError.text("Failed to leave room") }; if !success { return ResponseCode::BadRequest.text("You are currently not in this room (how did this happen?)") } ResponseCode::Success.text("Successfully left room") } pub const CHAT_SEND: EndpointDocumentation = EndpointDocumentation { uri: "/api/chat/send", method: EndpointMethod::Post, description: "Send a message to a room", body: Some( r#" { "room_id": 420, "content" : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } "#, ), responses: &[ (201, "Successfully sent message"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to send message"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct SendMessageRequest { room_id: u64, content: String } impl Check for SendMessageRequest { fn check(&self) -> CheckResult { Self::assert_length( &self.content, 1, 500, "Messages must be between 1-500 length" )?; Ok(()) } } async fn send ( AuthorizedUser(user): AuthorizedUser, Database(db): Database, Json(body): Json, ) -> Response { let Ok(room) = ChatRoom::from_user_and_room_id(&db, user.user_id, body.room_id) else { return ResponseCode::BadRequest.text("Room doesnt exist or you are not in it") }; let Ok(_msg) = room.send_message(&db, user.user_id, body.content) else { return ResponseCode::InternalServerError.text("Failed to send message") }; ResponseCode::Created.text("Successfully sent message") } pub const CHAT_LOAD: EndpointDocumentation = EndpointDocumentation { uri: "/api/chat/load", method: EndpointMethod::Post, description: "Get a page of historic room messages starting before given message id", body: Some( r#" { "room_id": 69, "newest_msg": 400, "page": 3 } "#, ), responses: &[ (201, "Successfully sent message"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to send message"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct LoadMessagesRequest { room_id: u64, newest_msg: u64, page: u64 } impl Check for LoadMessagesRequest { fn check(&self) -> CheckResult { Ok(()) } } async fn load ( AuthorizedUser(user): AuthorizedUser, Database(db): Database, Json(body): Json, ) -> Response { let Ok(room) = ChatRoom::from_user_and_room_id(&db, user.user_id, body.room_id) else { return ResponseCode::BadRequest.text("Room doesnt exist or you are not in it") }; let Ok(msgs) = room.load_old_chat_messagegs(&db, body.newest_msg, body.page) else { return ResponseCode::InternalServerError.text("Failed to load messages") }; let Ok(json) = serde_json::to_string(&msgs) else { return ResponseCode::InternalServerError.text("Failed to load messages") }; ResponseCode::Created.json(&json) } pub fn router() -> Router { Router::new() .route("/create", post(create)) .route("/list", post(list)) .route("/add", patch(add)) .route("/leave", delete(leave)) .route("/send", post(send)) .route("/load", post(load)) }