use axum::{ response::Response, routing::{patch, post}, Router, }; use serde::Deserialize; use crate::{ public::docs::{EndpointDocumentation, EndpointMethod}, types::{ comment::Comment, extract::{AuthorizedUser, Check, CheckResult, Json}, http::ResponseCode, like::Like, post::Post, }, }; pub const POSTS_CREATE: EndpointDocumentation = EndpointDocumentation { uri: "/api/posts/create", method: EndpointMethod::Post, description: "Creates a new post", body: Some( r#" { "content" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." } "#, ), responses: &[ (201, "Successfully created post"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to create post"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct PostCreateRequest { content: String, } impl Check for PostCreateRequest { fn check(&self) -> CheckResult { Self::assert_length( &self.content, 1, 500, "Comments must be between 1-500 characters long", )?; Ok(()) } } async fn create( AuthorizedUser(user): AuthorizedUser, Json(body): Json, ) -> Response { let Ok(post) = Post::new(user.user_id, body.content) else { return ResponseCode::InternalServerError.text("Failed to create post") }; let Ok(json) = serde_json::to_string(&post) else { return ResponseCode::InternalServerError.text("Failed to create post") }; ResponseCode::Created.json(&json) } pub const POSTS_PAGE: EndpointDocumentation = EndpointDocumentation { uri: "/api/posts/page", method: EndpointMethod::Post, description: "Load a section of posts from newest to oldest", body: Some( r#" { "page": 0 } "#, ), responses: &[ (200, "Returns posts in application/json"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to fetch posts"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct PostPageRequest { page: u64, } impl Check for PostPageRequest { fn check(&self) -> CheckResult { Ok(()) } } async fn page( AuthorizedUser(_user): AuthorizedUser, Json(body): Json, ) -> Response { let Ok(posts) = Post::from_post_page(body.page) else { return ResponseCode::InternalServerError.text("Failed to fetch posts") }; let Ok(json) = serde_json::to_string(&posts) else { return ResponseCode::InternalServerError.text("Failed to fetch posts") }; ResponseCode::Success.json(&json) } pub const COMMENTS_PAGE: EndpointDocumentation = EndpointDocumentation { uri: "/api/posts/comments", method: EndpointMethod::Post, description: "Load a section of comments from newest to oldest", body: Some( r#" { "page": 1, "post_id": 13 } "#, ), responses: &[ (200, "Returns comments in application/json"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to fetch comments"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct CommentsPageRequest { page: u64, post_id: u64 } impl Check for CommentsPageRequest { fn check(&self) -> CheckResult { Ok(()) } } async fn comments( AuthorizedUser(_user): AuthorizedUser, Json(body): Json, ) -> Response { let Ok(comments) = Comment::from_comment_page(body.page, body.post_id) else { return ResponseCode::InternalServerError.text("Failed to fetch comments") }; let Ok(json) = serde_json::to_string(&comments) else { return ResponseCode::InternalServerError.text("Failed to fetch comments") }; ResponseCode::Success.json(&json) } pub const POSTS_USER: EndpointDocumentation = EndpointDocumentation { uri: "/api/posts/user", method: EndpointMethod::Post, description: "Load a section of posts from newest to oldest from a specific user", body: Some( r#" { "user_id": 3, "page": 0 } "#, ), responses: &[ (200, "Returns posts in application/json"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to fetch posts"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct UsersPostsRequest { user_id: u64, page: u64, } impl Check for UsersPostsRequest { fn check(&self) -> CheckResult { Ok(()) } } async fn user( AuthorizedUser(_user): AuthorizedUser, Json(body): Json, ) -> Response { let Ok(posts) = Post::from_user_post_page(body.user_id, body.page) else { return ResponseCode::InternalServerError.text("Failed to fetch posts") }; let Ok(json) = serde_json::to_string(&posts) else { return ResponseCode::InternalServerError.text("Failed to fetch posts") }; ResponseCode::Success.json(&json) } pub const POSTS_COMMENT: EndpointDocumentation = EndpointDocumentation { uri: "/api/posts/comment", method: EndpointMethod::Patch, description: "Add a comment to a post", body: Some( r#" { "content": "This is a very cool comment", "post_id": 0 } "#, ), responses: &[ (200, "Successfully added comment"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to add comment"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct PostCommentRequest { content: String, post_id: u64, } impl Check for PostCommentRequest { fn check(&self) -> CheckResult { Self::assert_length( &self.content, 1, 255, "Comments must be between 1-255 characters long", )?; Ok(()) } } async fn comment( AuthorizedUser(user): AuthorizedUser, Json(body): Json, ) -> Response { if let Err(err) = Comment::new(user.user_id, body.post_id, &body.content) { return err; } ResponseCode::Success.text("Successfully commented on post") } pub const POSTS_LIKE: EndpointDocumentation = EndpointDocumentation { uri: "/api/posts/like", method: EndpointMethod::Patch, description: "Set like status on a post", body: Some( r#" { "post_id" : 0, "status" : true } "#, ), responses: &[ (200, "Successfully set like status"), (400, "Body does not match parameters"), (401, "Unauthorized"), (500, "Failed to set like status"), ], cookie: Some("auth"), }; #[derive(Deserialize)] struct PostLikeRequest { state: bool, post_id: u64, } impl Check for PostLikeRequest { fn check(&self) -> CheckResult { Ok(()) } } async fn like(AuthorizedUser(user): AuthorizedUser, Json(body): Json) -> Response { if body.state { if let Err(err) = Like::add_liked(user.user_id, body.post_id) { return err; } } else if let Err(err) = Like::remove_liked(user.user_id, body.post_id) { return err; } ResponseCode::Success.text("Successfully changed like status on post") } pub fn router() -> Router { Router::new() .route("/create", post(create)) .route("/page", post(page)) .route("/comments", post(comments)) .route("/user", post(user)) .route("/comment", patch(comment)) .route("/like", patch(like)) }