summaryrefslogtreecommitdiff
path: root/src/api
diff options
context:
space:
mode:
authorTyler Murphy <=>2023-07-26 01:04:39 -0400
committerTyler Murphy <=>2023-07-26 01:04:39 -0400
commita2c89301d5c63f573ac6680f9c11acda69043a78 (patch)
tree5d3cccf39438a41f9465e87e06c8bb43834d12f6 /src/api
parentallow port env (diff)
downloadxssbook-a2c89301d5c63f573ac6680f9c11acda69043a78.tar.gz
xssbook-a2c89301d5c63f573ac6680f9c11acda69043a78.tar.bz2
xssbook-a2c89301d5c63f573ac6680f9c11acda69043a78.zip
start dms
Diffstat (limited to 'src/api')
-rw-r--r--src/api/chat.rs324
-rw-r--r--src/api/mod.rs33
2 files changed, 330 insertions, 27 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))
+}
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 8b631c8..0c01ea0 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -1,33 +1,21 @@
use crate::types::extract::{RouterURI, self};
-use axum::{
- error_handling::HandleErrorLayer,
- BoxError, Extension, Router, middleware,
-};
-use tower::ServiceBuilder;
-use tower_governor::{
- errors::display_error, governor::GovernorConfigBuilder, key_extractor::SmartIpKeyExtractor,
- GovernorLayer,
-};
+pub mod chat;
pub mod admin;
pub mod auth;
pub mod posts;
pub mod users;
pub use auth::RegistrationRequet;
+use axum::{Extension, Router, middleware};
pub fn router() -> Router {
- let governor_conf = Box::new(
- GovernorConfigBuilder::default()
- .burst_size(15)
- .per_second(1)
- .key_extractor(SmartIpKeyExtractor)
- .finish()
- .expect("Failed to create rate limiter"),
- );
-
Router::new()
.nest(
+ "/chat",
+ chat::router().layer(Extension(RouterURI("/api/chat"))),
+ )
+ .nest(
"/admin",
admin::router().layer(Extension(RouterURI("/api/admin"))),
)
@@ -43,14 +31,5 @@ pub fn router() -> Router {
"/posts",
posts::router().layer(Extension(RouterURI("/api/posts"))),
)
- .layer(
- ServiceBuilder::new()
- .layer(HandleErrorLayer::new(|e: BoxError| async move {
- display_error(e)
- }))
- .layer(GovernorLayer {
- config: Box::leak(governor_conf),
- }),
- )
.layer(middleware::from_fn(extract::connect))
}