Skip to main content

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    async fn fetch_owned_servers(&self, user_id: &str) -> Result<Vec<Server>> {
47        query!(
48            self,
49            find,
50            COL,
51            doc! {
52                "owner": user_id
53            }
54        )
55    }
56
57    /// Update a server with new information
58    async fn update_server(
59        &self,
60        id: &str,
61        partial: &PartialServer,
62        remove: Vec<FieldsServer>,
63    ) -> Result<()> {
64        query!(
65            self,
66            update_one_by_id,
67            COL,
68            id,
69            partial,
70            remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
71            None
72        )
73        .map(|_| ())
74    }
75
76    /// Delete a server by its id
77    async fn delete_server(&self, id: &str) -> Result<()> {
78        self.delete_associated_server_objects(id).await?;
79        query!(self, delete_one_by_id, COL, id).map(|_| ())
80    }
81
82    /// Insert a new role into server object
83    async fn insert_role(&self, server_id: &str, role: &Role) -> Result<()> {
84        self.col::<Document>(COL)
85            .update_one(
86                doc! {
87                    "_id": server_id
88                },
89                doc! {
90                    "$set": {
91                        "roles.".to_owned() + role.id.as_str(): to_document(role)
92                            .map_err(|_| create_database_error!("to_document", "role"))?
93                    }
94                },
95            )
96            .await
97            .map(|_| ())
98            .map_err(|_| create_database_error!("update_one", "server"))
99    }
100
101    /// Update an existing role on a server
102    async fn update_role(
103        &self,
104        server_id: &str,
105        role_id: &str,
106        partial: &PartialRole,
107        remove: Vec<FieldsRole>,
108    ) -> Result<()> {
109        query!(
110            self,
111            update_one_by_id,
112            COL,
113            server_id,
114            partial,
115            remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
116            "roles.".to_owned() + role_id + "."
117        )
118        .map(|_| ())
119    }
120
121    /// Delete a role from a server
122    ///
123    /// Also updates channels and members.
124    async fn delete_role(&self, server_id: &str, role_id: &str) -> Result<()> {
125        self.col::<Document>("server_members")
126            .update_many(
127                doc! {
128                    "_id.server": server_id
129                },
130                doc! {
131                    "$pull": {
132                        "roles": &role_id
133                    }
134                },
135            )
136            .await
137            .map_err(|_| create_database_error!("update_many", "server_members"))?;
138
139        self.col::<Document>("channels")
140            .update_one(
141                doc! {
142                    "server": server_id
143                },
144                doc! {
145                    "$unset": {
146                        "role_permissions.".to_owned() + role_id: 1_i32
147                    }
148                },
149            )
150            .await
151            .map_err(|_| create_database_error!("update_one", "channels"))?;
152
153        self.col::<Document>("servers")
154            .update_one(
155                doc! {
156                    "_id": server_id
157                },
158                doc! {
159                    "$unset": {
160                        "roles.".to_owned() + role_id: 1_i32
161                    }
162                },
163            )
164            .await
165            .map(|_| ())
166            .map_err(|_| create_database_error!("update_one", "servers"))
167    }
168}
169
170impl IntoDocumentPath for FieldsServer {
171    fn as_path(&self) -> Option<&'static str> {
172        Some(match self {
173            FieldsServer::Banner => "banner",
174            FieldsServer::Categories => "categories",
175            FieldsServer::Description => "description",
176            FieldsServer::Icon => "icon",
177            FieldsServer::SystemMessages => "system_messages",
178        })
179    }
180}
181
182impl IntoDocumentPath for FieldsRole {
183    fn as_path(&self) -> Option<&'static str> {
184        Some(match self {
185            FieldsRole::Colour => "colour",
186            FieldsRole::Icon => "icon",
187        })
188    }
189}
190
191impl MongoDb {
192    pub async fn delete_associated_server_objects(&self, server_id: &str) -> Result<()> {
193        // Find all channels
194        let channels: Vec<String> = self
195            .col::<Document>("channels")
196            .find(doc! {
197                "server": server_id
198            })
199            .await
200            .map_err(|_| create_database_error!("find", "channels"))?
201            .filter_map(|s| async {
202                s.map(|d| d.get_str("_id").map(|s| s.to_string()).ok())
203                    .ok()
204                    .flatten()
205            })
206            .collect()
207            .await;
208
209        // Check if there are any attachments we need to delete.
210        self.delete_bulk_messages(doc! {
211            "channel": {
212                "$in": &channels
213            }
214        })
215        .await?;
216
217        // Delete all emoji.
218        self.col::<Document>("emojis")
219            .update_many(
220                doc! {
221                    "parent.id": &server_id
222                },
223                doc! {
224                    "$set": {
225                        "parent": {
226                            "type": "Detached"
227                        }
228                    }
229                },
230            )
231            .await
232            .map_err(|_| create_database_error!("update_many", "emojis"))?;
233
234        // Delete all channels.
235        self.col::<Document>("channels")
236            .delete_many(doc! {
237                "server": &server_id
238            })
239            .await
240            .map_err(|_| create_database_error!("delete_many", "channels"))?;
241
242        // Delete any associated objects, e.g. unreads and invites.
243        self.delete_associated_channel_objects(Bson::Document(doc! { "$in": &channels }))
244            .await?;
245
246        // Delete members and bans.
247        for with in &["server_members", "server_bans"] {
248            self.col::<Document>(with)
249                .delete_many(doc! {
250                    "_id.server": &server_id
251                })
252                .await
253                .map_err(|_| create_database_error!("delete_many", with))?;
254        }
255
256        // Update many attachments with parent id.
257        self.delete_many_attachments(doc! {
258            "used_for.id": &server_id
259        })
260        .await?;
261
262        self.col::<Document>("audit_logs")
263            .delete_many(doc! {
264                "server": &server_id
265            })
266            .await
267            .map_err(|_| create_database_error!("delete_many", "audit_logs"))?;
268
269        Ok(())
270    }
271}