diff options
Diffstat (limited to 'src/api/chat.rs')
-rw-r--r-- | src/api/chat.rs | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/src/api/chat.rs b/src/api/chat.rs new file mode 100644 index 0000000..0f3b92e --- /dev/null +++ b/src/api/chat.rs @@ -0,0 +1,324 @@ +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<RoomCreateRequest>, +) -> 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<AddUserRequest>, +) -> 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<LeaveRoomRequest>, +) -> 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<SendMessageRequest>, +) -> 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<LoadMessagesRequest>, +) -> 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)) +} |