From dbd70abaca7501e6ac302861b88258a791eed943 Mon Sep 17 00:00:00 2001
From: Paul <paulmakles@gmail.com>
Date: Sun, 6 Jun 2021 10:17:33 +0100
Subject: [PATCH] Servers: Add route for fetching members.

---
 src/database/entities/server.rs     | 31 ++++++++++++++++++++++++++---
 src/database/entities/user.rs       |  1 +
 src/notifications/events.rs         | 25 +----------------------
 src/routes/servers/members_fetch.rs | 23 +++++++++++++++++++++
 src/routes/servers/mod.rs           |  5 ++++-
 5 files changed, 57 insertions(+), 28 deletions(-)
 create mode 100644 src/routes/servers/members_fetch.rs

diff --git a/src/database/entities/server.rs b/src/database/entities/server.rs
index fefdc0e..ff1d48e 100644
--- a/src/database/entities/server.rs
+++ b/src/database/entities/server.rs
@@ -42,9 +42,6 @@ pub struct Server {
     pub owner: String,
 
     pub name: String,
-    // pub default_permissions: u32,
-    // pub invites: Vec<Invite>,
-    // pub bans: Vec<Ban>,
     pub channels: Vec<String>,
 
     #[serde(skip_serializing_if = "Option::is_none")]
@@ -90,6 +87,8 @@ impl Server {
         let messages = get_collection("messages");
 
         // Check if there are any attachments we need to delete.
+        // ! FIXME: make this generic and merge with channel delete
+        // ! e.g. delete_channel(filter: doc!)
         let message_ids = messages
             .find(
                 doc! {
@@ -206,6 +205,32 @@ impl Server {
         Ok(())
     }
 
+    pub async fn fetch_members(id: &str) -> Result<Vec<String>> {
+        Ok(get_collection("server_members")
+            .find(
+                doc! {
+                    "_id.server": id
+                },
+                None,
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "find",
+                with: "server_members",
+            })?
+            .filter_map(async move |s| s.ok())
+            .collect::<Vec<Document>>()
+            .await
+            .into_iter()
+            .filter_map(|x| {
+                x.get_document("_id")
+                    .ok()
+                    .map(|i| i.get_str("user").ok().map(|x| x.to_string()))
+            })
+            .flatten()
+            .collect::<Vec<String>>())
+    }
+
     pub async fn join_member(&self, id: &str) -> Result<()> {
         get_collection("server_members")
             .insert_one(
diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs
index 7833ccd..21c427d 100644
--- a/src/database/entities/user.rs
+++ b/src/database/entities/user.rs
@@ -184,6 +184,7 @@ impl User {
     }
 
     /// Utility function for fetching multiple users from the perspective of one.
+    /// Assumes user has a mutual connection with others.
     pub async fn fetch_multiple_users(&self, user_ids: Vec<String>) -> Result<Vec<User>> {
         let mut users = vec![];
         let mut cursor = get_collection("users")
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index f51c329..a78a312 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -166,30 +166,7 @@ pub async fn prehandle_hook(notification: &ClientboundNotification) -> Result<()
                 }
                 Channel::TextChannel { server, .. } => {
                     // ! FIXME: write a better algorithm?
-                    let members = get_collection("server_members")
-                        .find(
-                            doc! {
-                                "_id.server": server
-                            },
-                            None,
-                        )
-                        .await
-                        .map_err(|_| Error::DatabaseError {
-                            operation: "find",
-                            with: "server_members",
-                        })?
-                        .filter_map(async move |s| s.ok())
-                        .collect::<Vec<Document>>()
-                        .await
-                        .into_iter()
-                        .filter_map(|x| {
-                            x.get_document("_id")
-                                .ok()
-                                .map(|i| i.get_str("user").ok().map(|x| x.to_string()))
-                        })
-                        .flatten()
-                        .collect::<Vec<String>>();
-
+                    let members = Server::fetch_members(server).await?;
                     for member in members {
                         subscribe_if_exists(member.clone(), channel_id.to_string()).ok();
                     }
diff --git a/src/routes/servers/members_fetch.rs b/src/routes/servers/members_fetch.rs
new file mode 100644
index 0000000..dfe9a37
--- /dev/null
+++ b/src/routes/servers/members_fetch.rs
@@ -0,0 +1,23 @@
+use crate::database::*;
+use crate::util::result::{Error, Result};
+
+use rocket_contrib::json::JsonValue;
+
+// ! FIXME: this is a temporary route while permissions are being worked on.
+
+#[get("/<target>/members")]
+pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
+    let target = target.fetch_server().await?;
+
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_server(&target)
+        .for_server()
+        .await?;
+    
+    if !perm.get_view() {
+        Err(Error::MissingPermission)?
+    }
+
+    let members = Server::fetch_members(&target.id).await?;
+    Ok(json!(user.fetch_multiple_users(members).await?))
+}
diff --git a/src/routes/servers/mod.rs b/src/routes/servers/mod.rs
index 20ef08b..b668399 100644
--- a/src/routes/servers/mod.rs
+++ b/src/routes/servers/mod.rs
@@ -7,12 +7,15 @@ mod server_edit;
 
 mod channel_create;
 
+mod members_fetch;
+
 pub fn routes() -> Vec<Route> {
     routes![
         server_create::req,
         server_delete::req,
         server_fetch::req,
         server_edit::req,
-        channel_create::req
+        channel_create::req,
+        members_fetch::req
     ]
 }
-- 
GitLab