From d99c87d6da196cb83825ee06b60237f2d04cd633 Mon Sep 17 00:00:00 2001
From: Paul Makles <paulmakles@gmail.com>
Date: Mon, 13 Jul 2020 19:49:50 +0100
Subject: [PATCH] Change message fetching to query messages.

---
 src/email.rs          |  6 +---
 src/routes/account.rs | 29 ++++++++++++-----
 src/routes/channel.rs | 74 +++++++++++++++++++++++++++++++++++++------
 src/routes/guild.rs   | 50 +++++++++++++++++++++++------
 src/routes/mod.rs     | 11 +------
 src/routes/user.rs    | 34 ++++++++++++++------
 6 files changed, 153 insertions(+), 51 deletions(-)

diff --git a/src/email.rs b/src/email.rs
index bf4f3cd..43cc7b0 100644
--- a/src/email.rs
+++ b/src/email.rs
@@ -18,11 +18,7 @@ pub fn send_email(target: String, subject: String, body: String, html: String) -
     map.insert("html", html);
 
     let client = Client::new();
-    match client
-        .post(&portal())
-        .json(&map)
-        .send()
-    {
+    match client.post(&portal()).json(&map).send() {
         Ok(_) => Ok(()),
         Err(_) => Err(()),
     }
diff --git a/src/routes/account.rs b/src/routes/account.rs
index dff40f6..4a03951 100644
--- a/src/routes/account.rs
+++ b/src/routes/account.rs
@@ -204,9 +204,7 @@ pub fn resend_email(info: Json<Resend>) -> Response {
             }
         }
     } else {
-        Response::NotFound(
-            json!({ "error": "Email not found or pending verification!" }),
-        )
+        Response::NotFound(json!({ "error": "Email not found or pending verification!" }))
     }
 }
 
@@ -291,8 +289,23 @@ pub fn token(info: Json<Token>) -> Response {
     }
 }
 
-#[options("/create")] pub fn create_preflight() -> Response { Response::Result(super::Status::Ok) }
-#[options("/verify/<_code>")] pub fn verify_email_preflight(_code: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/resend")] pub fn resend_email_preflight() -> Response { Response::Result(super::Status::Ok) }
-#[options("/login")] pub fn login_preflight() -> Response { Response::Result(super::Status::Ok) }
-#[options("/token")] pub fn token_preflight() -> Response { Response::Result(super::Status::Ok) }
+#[options("/create")]
+pub fn create_preflight() -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/verify/<_code>")]
+pub fn verify_email_preflight(_code: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/resend")]
+pub fn resend_email_preflight() -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/login")]
+pub fn login_preflight() -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/token")]
+pub fn token_preflight() -> Response {
+    Response::Result(super::Status::Ok)
+}
diff --git a/src/routes/channel.rs b/src/routes/channel.rs
index eba1d5c..e6f5b8f 100644
--- a/src/routes/channel.rs
+++ b/src/routes/channel.rs
@@ -15,6 +15,7 @@ use bson::{doc, from_bson, Bson, Bson::UtcDatetime};
 use chrono::prelude::*;
 use mongodb::options::FindOptions;
 use num_enum::TryFromPrimitive;
+use rocket::request::Form;
 use rocket_contrib::json::Json;
 use serde::{Deserialize, Serialize};
 use ulid::Ulid;
@@ -478,17 +479,55 @@ pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> {
     }
 }
 
+#[derive(Serialize, Deserialize, FromForm)]
+pub struct MessageFetchOptions {
+    limit: Option<i64>,
+    before: Option<String>,
+    after: Option<String>,
+}
+
 /// fetch channel messages
-#[get("/<target>/messages")]
-pub fn messages(user: UserRef, target: ChannelRef) -> Option<Response> {
+#[get("/<target>/messages?<options..>")]
+pub fn messages(
+    user: UserRef,
+    target: ChannelRef,
+    options: Form<MessageFetchOptions>,
+) -> Option<Response> {
     let permissions = with_permissions!(user, target);
 
     if !permissions.get_read_messages() {
         return Some(Response::LackingPermission(Permission::ReadMessages));
     }
 
+    // ! FIXME: update wiki to reflect changes
+    let mut query = doc! { "channel": target.id };
+
+    if let Some(before) = &options.before {
+        query.insert("_id", doc! { "$lte": before });
+    }
+
+    if let Some(after) = &options.after {
+        query.insert("_id", doc! { "$gte": after });
+    }
+
+    let limit = if let Some(limit) = options.limit {
+        limit.min(100).max(0)
+    } else {
+        50
+    };
+
     let col = database::get_collection("messages");
-    let result = col.find(doc! { "channel": target.id }, None).unwrap();
+    let result = col
+        .find(
+            query,
+            FindOptions::builder()
+                .limit(limit)
+                .sort(doc! {
+                    "_id": -1
+                })
+                .build(),
+        )
+        .unwrap();
 
     let mut messages = Vec::new();
     for item in result {
@@ -532,7 +571,9 @@ pub fn send_message(
     let nonce: String = message.nonce.chars().take(32).collect();
 
     if content.len() == 0 {
-        return Some(Response::NotAcceptable(json!({ "error": "No message content!" })));
+        return Some(Response::NotAcceptable(
+            json!({ "error": "No message content!" }),
+        ));
     }
 
     let col = database::get_collection("messages");
@@ -687,8 +728,23 @@ pub fn delete_message(user: UserRef, target: ChannelRef, message: Message) -> Op
     }
 }
 
-#[options("/create")] pub fn create_group_preflight() -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>")] pub fn channel_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/recipients/<_member>")] pub fn member_preflight(_target: String, _member: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/messages")] pub fn messages_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/messages/<_message>")] pub fn message_preflight(_target: String, _message: String) -> Response { Response::Result(super::Status::Ok) }
+#[options("/create")]
+pub fn create_group_preflight() -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>")]
+pub fn channel_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/recipients/<_member>")]
+pub fn member_preflight(_target: String, _member: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/messages")]
+pub fn messages_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/messages/<_message>")]
+pub fn message_preflight(_target: String, _message: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
diff --git a/src/routes/guild.rs b/src/routes/guild.rs
index 5e8cfe0..9c27208 100644
--- a/src/routes/guild.rs
+++ b/src/routes/guild.rs
@@ -839,13 +839,43 @@ pub fn unban_member(user: UserRef, target: GuildRef, other: String) -> Option<Re
     }
 }
 
-#[options("/<_target>")] pub fn guild_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/channels")] pub fn create_channel_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/channels/<_channel>/invite")] pub fn create_invite_preflight(_target: String, _channel: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/invites/<_code>")] pub fn remove_invite_preflight(_target: String, _code: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/invites")] pub fn fetch_invites_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/join/<_code>", rank = 1)] pub fn invite_preflight(_code: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/create")] pub fn create_guild_preflight() -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/members")] pub fn fetch_members_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/members/<_other>")] pub fn fetch_member_preflight(_target: String, _other: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/members/<_other>/ban")] pub fn ban_member_preflight(_target: String, _other: String) -> Response { Response::Result(super::Status::Ok) }
+#[options("/<_target>")]
+pub fn guild_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/channels")]
+pub fn create_channel_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/channels/<_channel>/invite")]
+pub fn create_invite_preflight(_target: String, _channel: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/invites/<_code>")]
+pub fn remove_invite_preflight(_target: String, _code: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/invites")]
+pub fn fetch_invites_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/join/<_code>", rank = 1)]
+pub fn invite_preflight(_code: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/create")]
+pub fn create_guild_preflight() -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/members")]
+pub fn fetch_members_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/members/<_other>")]
+pub fn fetch_member_preflight(_target: String, _other: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/members/<_other>/ban")]
+pub fn ban_member_preflight(_target: String, _other: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
diff --git a/src/routes/mod.rs b/src/routes/mod.rs
index 88422d9..14889c3 100644
--- a/src/routes/mod.rs
+++ b/src/routes/mod.rs
@@ -63,12 +63,7 @@ impl<'a> rocket::response::Responder<'a> for Permission {
 
 pub fn mount(rocket: Rocket) -> Rocket {
     rocket
-        .mount("/",
-            routes![
-                root::root,
-                root::teapot
-            ]
-        )
+        .mount("/", routes![root::root, root::teapot])
         .mount(
             "/account",
             routes![
@@ -77,7 +72,6 @@ pub fn mount(rocket: Rocket) -> Rocket {
                 account::resend_email,
                 account::login,
                 account::token,
-
                 account::create_preflight,
                 account::verify_email_preflight,
                 account::resend_email_preflight,
@@ -99,7 +93,6 @@ pub fn mount(rocket: Rocket) -> Rocket {
                 user::remove_friend,
                 user::block_user,
                 user::unblock_user,
-
                 user::user_preflight,
                 user::lookup_preflight,
                 user::dms_preflight,
@@ -121,7 +114,6 @@ pub fn mount(rocket: Rocket) -> Rocket {
                 channel::send_message,
                 channel::edit_message,
                 channel::delete_message,
-
                 channel::create_group_preflight,
                 channel::channel_preflight,
                 channel::member_preflight,
@@ -147,7 +139,6 @@ pub fn mount(rocket: Rocket) -> Rocket {
                 guild::kick_member,
                 guild::ban_member,
                 guild::unban_member,
-
                 guild::guild_preflight,
                 guild::create_channel_preflight,
                 guild::create_invite_preflight,
diff --git a/src/routes/user.rs b/src/routes/user.rs
index 16748df..d71f0a6 100644
--- a/src/routes/user.rs
+++ b/src/routes/user.rs
@@ -424,9 +424,7 @@ pub fn block_user(user: UserRef, target: UserRef) -> Response {
     let col = database::get_collection("users");
 
     match get_relationship(&user, &target) {
-        Relationship::Friend
-        | Relationship::Incoming
-        | Relationship::Outgoing => {
+        Relationship::Friend | Relationship::Incoming | Relationship::Outgoing => {
             if col
                 .update_one(
                     doc! {
@@ -707,9 +705,27 @@ pub fn unblock_user(user: UserRef, target: UserRef) -> Response {
     }
 }
 
-#[options("/<_target>")] pub fn user_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/lookup")] pub fn lookup_preflight() -> Response { Response::Result(super::Status::Ok) }
-#[options("/@me/dms")] pub fn dms_preflight() -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/dm")] pub fn dm_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/friend")] pub fn friend_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
-#[options("/<_target>/block")] pub fn block_user_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) }
+#[options("/<_target>")]
+pub fn user_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/lookup")]
+pub fn lookup_preflight() -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/@me/dms")]
+pub fn dms_preflight() -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/dm")]
+pub fn dm_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/friend")]
+pub fn friend_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
+#[options("/<_target>/block")]
+pub fn block_user_preflight(_target: String) -> Response {
+    Response::Result(super::Status::Ok)
+}
-- 
GitLab