revolt_database/models/servers/ops/
mongodb.rs

1use bson::{to_document, Bson, Document};
2use futures::StreamExt;
3use revolt_result::Result;
4
5use crate::{FieldsRole, FieldsServer, PartialRole, PartialServer, Role, Server};
6use crate::{IntoDocumentPath, MongoDb};
7
8use super::AbstractServers;
9
10static COL: &str = "servers";
11
12#[async_trait]
13impl AbstractServers for MongoDb {
14    /// Insert a new server into database
15    async fn insert_server(&self, server: &Server) -> Result<()> {
16        query!(self, insert_one, COL, &server).map(|_| ())
17    }
18
19    /// Fetch a server by its id
20    async fn fetch_server(&self, id: &str) -> Result<Server> {
21        query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(NotFound))
22    }
23
24    /// Fetch a servers by their ids
25    async fn fetch_servers<'a>(&self, ids: &'a [String]) -> Result<Vec<Server>> {
26        Ok(self
27            .col::<Server>(COL)
28            .find(doc! {
29                "_id": {
30                    "$in": ids
31                }
32            })
33            .await
34            .map_err(|_| create_database_error!("find", "servers"))?
35            .filter_map(|s| async {
36                if cfg!(debug_assertions) {
37                    Some(s.unwrap())
38                } else {
39                    s.ok()
40                }
41            })
42            .collect()
43            .await)
44    }
45
46    /// Update a server with new information
47    async fn update_server(
48        &self,
49        id: &str,
50        partial: &PartialServer,
51        remove: Vec<FieldsServer>,
52    ) -> Result<()> {
53        query!(
54            self,
55            update_one_by_id,
56            COL,
57            id,
58            partial,
59            remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
60            None
61        )
62        .map(|_| ())
63    }
64
65    /// Delete a server by its id
66    async fn delete_server(&self, id: &str) -> Result<()> {
67        self.delete_associated_server_objects(id).await?;
68        query!(self, delete_one_by_id, COL, id).map(|_| ())
69    }
70
71    /// Insert a new role into server object
72    async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()> {
73        self.col::<Document>(COL)
74            .update_one(
75                doc! {
76                    "_id": server_id
77                },
78                doc! {
79                    "$set": {
80                        "roles.".to_owned() + role_id: to_document(role)
81                            .map_err(|_| create_database_error!("to_document", "role"))?
82                    }
83                },
84            )
85            .await
86            .map(|_| ())
87            .map_err(|_| create_database_error!("update_one", "server"))
88    }
89
90    /// Update an existing role on a server
91    async fn update_role(
92        &self,
93        server_id: &str,
94        role_id: &str,
95        partial: &PartialRole,
96        remove: Vec<FieldsRole>,
97    ) -> Result<()> {
98        query!(
99            self,
100            update_one_by_id,
101            COL,
102            server_id,
103            partial,
104            remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
105            "roles.".to_owned() + role_id + "."
106        )
107        .map(|_| ())
108    }
109
110    /// Delete a role from a server
111    ///
112    /// Also updates channels and members.
113    async fn delete_role(&self, server_id: &str, role_id: &str) -> Result<()> {
114        self.col::<Document>("server_members")
115            .update_many(
116                doc! {
117                    "_id.server": server_id
118                },
119                doc! {
120                    "$pull": {
121                        "roles": &role_id
122                    }
123                },
124            )
125            .await
126            .map_err(|_| create_database_error!("update_many", "server_members"))?;
127
128        self.col::<Document>("channels")
129            .update_one(
130                doc! {
131                    "server": server_id
132                },
133                doc! {
134                    "$unset": {
135                        "role_permissions.".to_owned() + role_id: 1_i32
136                    }
137                },
138            )
139            .await
140            .map_err(|_| create_database_error!("update_one", "channels"))?;
141
142        self.col::<Document>("servers")
143            .update_one(
144                doc! {
145                    "_id": server_id
146                },
147                doc! {
148                    "$unset": {
149                        "roles.".to_owned() + role_id: 1_i32
150                    }
151                },
152            )
153            .await
154            .map(|_| ())
155            .map_err(|_| create_database_error!("update_one", "servers"))
156    }
157}
158
159impl IntoDocumentPath for FieldsServer {
160    fn as_path(&self) -> Option<&'static str> {
161        Some(match self {
162            FieldsServer::Banner => "banner",
163            FieldsServer::Categories => "categories",
164            FieldsServer::Description => "description",
165            FieldsServer::Icon => "icon",
166            FieldsServer::SystemMessages => "system_messages",
167        })
168    }
169}
170
171impl IntoDocumentPath for FieldsRole {
172    fn as_path(&self) -> Option<&'static str> {
173        Some(match self {
174            FieldsRole::Colour => "colour",
175        })
176    }
177}
178
179impl MongoDb {
180    pub async fn delete_associated_server_objects(&self, server_id: &str) -> Result<()> {
181        // Find all channels
182        let channels: Vec<String> = self
183            .col::<Document>("channels")
184            .find(doc! {
185                "server": server_id
186            })
187            .await
188            .map_err(|_| create_database_error!("find", "channels"))?
189            .filter_map(|s| async {
190                s.map(|d| d.get_str("_id").map(|s| s.to_string()).ok())
191                    .ok()
192                    .flatten()
193            })
194            .collect()
195            .await;
196
197        // Check if there are any attachments we need to delete.
198        self.delete_bulk_messages(doc! {
199            "channel": {
200                "$in": &channels
201            }
202        })
203        .await?;
204
205        // Delete all emoji.
206        self.col::<Document>("emojis")
207            .update_many(
208                doc! {
209                    "parent.id": &server_id
210                },
211                doc! {
212                    "$set": {
213                        "parent": {
214                            "type": "Detached"
215                        }
216                    }
217                },
218            )
219            .await
220            .map_err(|_| create_database_error!("update_many", "emojis"))?;
221
222        // Delete all channels.
223        self.col::<Document>("channels")
224            .delete_many(doc! {
225                "server": &server_id
226            })
227            .await
228            .map_err(|_| create_database_error!("delete_many", "channels"))?;
229
230        // Delete any associated objects, e.g. unreads and invites.
231        self.delete_associated_channel_objects(Bson::Document(doc! { "$in": &channels }))
232            .await?;
233
234        // Delete members and bans.
235        for with in &["server_members", "server_bans"] {
236            self.col::<Document>(with)
237                .delete_many(doc! {
238                    "_id.server": &server_id
239                })
240                .await
241                .map_err(|_| create_database_error!("delete_many", with))?;
242        }
243
244        // Update many attachments with parent id.
245        self.delete_many_attachments(doc! {
246            "used_for.id": &server_id
247        })
248        .await?;
249
250        Ok(())
251    }
252}