revolt_database/models/servers/
model.rs1use std::collections::{HashMap, HashSet};
2
3use revolt_models::v0::{self, DataCreateServerChannel};
4use revolt_permissions::{OverrideField, DEFAULT_PERMISSION_SERVER};
5use revolt_result::Result;
6use ulid::Ulid;
7
8use crate::{events::client::EventV1, Channel, Database, File, User};
9
10auto_derived_partial!(
11 pub struct Server {
13 #[serde(rename = "_id")]
15 pub id: String,
16 pub owner: String,
18
19 pub name: String,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub description: Option<String>,
24
25 pub channels: Vec<String>,
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub categories: Option<Vec<Category>>,
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub system_messages: Option<SystemMessageChannels>,
34
35 #[serde(
37 default = "HashMap::<String, Role>::new",
38 skip_serializing_if = "HashMap::<String, Role>::is_empty"
39 )]
40 pub roles: HashMap<String, Role>,
41 pub default_permissions: i64,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub icon: Option<File>,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub banner: Option<File>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub flags: Option<i32>,
54
55 #[serde(skip_serializing_if = "crate::if_false", default)]
57 pub nsfw: bool,
58 #[serde(skip_serializing_if = "crate::if_false", default)]
60 pub analytics: bool,
61 #[serde(skip_serializing_if = "crate::if_false", default)]
63 pub discoverable: bool,
64 },
65 "PartialServer"
66);
67
68auto_derived_partial!(
69 pub struct Role {
71 #[serde(rename = "_id")]
73 pub id: String,
74 pub name: String,
76 pub permissions: OverrideField,
78 #[serde(skip_serializing_if = "Option::is_none")]
82 pub colour: Option<String>,
83 #[serde(skip_serializing_if = "crate::if_false", default)]
85 pub hoist: bool,
86 #[serde(default)]
88 pub rank: i64,
89 },
90 "PartialRole"
91);
92
93auto_derived!(
94 pub struct Category {
96 pub id: String,
98 pub title: String,
100 pub channels: Vec<String>,
102 }
103
104 pub struct SystemMessageChannels {
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub user_joined: Option<String>,
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub user_left: Option<String>,
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub user_kicked: Option<String>,
115 #[serde(skip_serializing_if = "Option::is_none")]
117 pub user_banned: Option<String>,
118 }
119
120 pub enum FieldsServer {
122 Description,
123 Categories,
124 SystemMessages,
125 Icon,
126 Banner,
127 }
128
129 pub enum FieldsRole {
131 Colour,
132 }
133);
134
135#[allow(clippy::disallowed_methods)]
136impl Server {
137 pub async fn create(
139 db: &Database,
140 data: v0::DataCreateServer,
141 owner: &User,
142 create_default_channels: bool,
143 ) -> Result<(Server, Vec<Channel>)> {
144 let mut server = Server {
145 id: ulid::Ulid::new().to_string(),
146 owner: owner.id.to_string(),
147 name: data.name,
148 description: data.description,
149 channels: vec![],
150 nsfw: data.nsfw.unwrap_or(false),
151 default_permissions: *DEFAULT_PERMISSION_SERVER as i64,
152
153 analytics: false,
154 banner: None,
155 categories: None,
156 discoverable: false,
157 flags: None,
158 icon: None,
159 roles: HashMap::new(),
160 system_messages: None,
161 };
162
163 let channels: Vec<Channel> = if create_default_channels {
164 vec![
165 Channel::create_server_channel(
166 db,
167 &mut server,
168 DataCreateServerChannel {
169 channel_type: v0::LegacyServerChannelType::Text,
170 name: "General".to_string(),
171 ..Default::default()
172 },
173 false,
174 )
175 .await?,
176 ]
177 } else {
178 vec![]
179 };
180
181 server.channels = channels.iter().map(|c| c.id().to_string()).collect();
182 db.insert_server(&server).await?;
183 Ok((server, channels))
184 }
185
186 pub async fn update(
188 &mut self,
189 db: &Database,
190 partial: PartialServer,
191 remove: Vec<FieldsServer>,
192 ) -> Result<()> {
193 for field in &remove {
194 self.remove_field(field);
195 }
196
197 self.apply_options(partial.clone());
198
199 db.update_server(&self.id, &partial, remove.clone()).await?;
200
201 EventV1::ServerUpdate {
202 id: self.id.clone(),
203 data: partial.into(),
204 clear: remove.into_iter().map(|v| v.into()).collect(),
205 }
206 .p(self.id.clone())
207 .await;
208
209 Ok(())
210 }
211
212 pub async fn delete(self, db: &Database) -> Result<()> {
214 EventV1::ServerDelete {
215 id: self.id.clone(),
216 }
217 .p(self.id.clone())
218 .await;
219
220 db.delete_server(&self.id).await
221 }
222
223 pub fn remove_field(&mut self, field: &FieldsServer) {
225 match field {
226 FieldsServer::Description => self.description = None,
227 FieldsServer::Categories => self.categories = None,
228 FieldsServer::SystemMessages => self.system_messages = None,
229 FieldsServer::Icon => self.icon = None,
230 FieldsServer::Banner => self.banner = None,
231 }
232 }
233
234 pub fn ordered_roles(&self) -> Vec<(String, Role)> {
236 let mut ordered_roles = self.roles.clone().into_iter().collect::<Vec<_>>();
237 ordered_roles.sort_by(|(_, role_a), (_, role_b)| role_a.rank.cmp(&role_b.rank));
238 ordered_roles
239 }
240
241 pub async fn set_role_permission(
243 &mut self,
244 db: &Database,
245 role_id: &str,
246 permissions: OverrideField,
247 ) -> Result<()> {
248 if let Some(role) = self.roles.get_mut(role_id) {
249 role.update(
250 db,
251 &self.id,
252 PartialRole {
253 permissions: Some(permissions),
254 ..Default::default()
255 },
256 vec![],
257 )
258 .await?;
259
260 Ok(())
261 } else {
262 Err(create_error!(NotFound))
263 }
264 }
265
266 pub async fn set_role_ordering(&mut self, db: &Database, new_order: Vec<String>) -> Result<()> {
268 debug_assert_eq!(self.roles.len(), new_order.len());
270
271 for (rank, id) in new_order.iter().enumerate() {
273 self.roles.get_mut(id).unwrap().rank = rank as i64;
274 }
275
276 db.update_server(
277 &self.id,
278 &PartialServer {
279 roles: Some(self.roles.clone()),
280 ..Default::default()
281 },
282 Vec::new(),
283 )
284 .await?;
285
286 EventV1::ServerRoleRanksUpdate {
288 id: self.id.clone(),
289 ranks: new_order,
290 }
291 .p(self.id.clone())
292 .await;
293
294 Ok(())
295 }
296}
297
298impl Role {
299 pub fn into_optional(self) -> PartialRole {
301 PartialRole {
302 id: Some(self.id),
303 name: Some(self.name),
304 permissions: Some(self.permissions),
305 colour: self.colour,
306 hoist: Some(self.hoist),
307 rank: Some(self.rank),
308 }
309 }
310
311 pub async fn create(db: &Database, server: &Server, name: String) -> Result<Self> {
313 let role = Role {
314 id: Ulid::new().to_string(),
315 name,
316 rank: server.roles.len() as i64,
318 colour: None,
319 hoist: false,
320 permissions: Default::default(),
321 };
322
323 db.insert_role(&server.id, &role).await?;
324
325 EventV1::ServerRoleUpdate {
326 id: server.id.clone(),
327 role_id: role.id.clone(),
328 data: role.clone().into_optional().into(),
329 clear: vec![],
330 }
331 .p(server.id.clone())
332 .await;
333
334 Ok(role)
335 }
336
337 pub async fn update(
339 &mut self,
340 db: &Database,
341 server_id: &str,
342 partial: PartialRole,
343 remove: Vec<FieldsRole>,
344 ) -> Result<()> {
345 for field in &remove {
346 self.remove_field(field);
347 }
348
349 self.apply_options(partial.clone());
350
351 db.update_role(server_id, &self.id, &partial, remove.clone())
352 .await?;
353
354 EventV1::ServerRoleUpdate {
355 id: server_id.to_string(),
356 role_id: self.id.clone(),
357 data: partial.into(),
358 clear: remove.into_iter().map(Into::into).collect(),
359 }
360 .p(server_id.to_string())
361 .await;
362
363 Ok(())
364 }
365
366 pub fn remove_field(&mut self, field: &FieldsRole) {
368 match field {
369 FieldsRole::Colour => self.colour = None,
370 }
371 }
372
373 pub async fn delete(self, db: &Database, server_id: &str) -> Result<()> {
375 EventV1::ServerRoleDelete {
376 id: server_id.to_string(),
377 role_id: self.id.clone(),
378 }
379 .p(server_id.to_string())
380 .await;
381
382 db.delete_role(server_id, &self.id).await
383 }
384}
385
386impl SystemMessageChannels {
387 pub fn into_channel_ids(self) -> HashSet<String> {
388 let mut ids = HashSet::new();
389
390 if let Some(id) = self.user_joined {
391 ids.insert(id);
392 }
393
394 if let Some(id) = self.user_left {
395 ids.insert(id);
396 }
397
398 if let Some(id) = self.user_kicked {
399 ids.insert(id);
400 }
401
402 if let Some(id) = self.user_banned {
403 ids.insert(id);
404 }
405
406 ids
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use revolt_permissions::{calculate_server_permissions, ChannelPermission};
413
414 use crate::{fixture, util::permissions::DatabasePermissionQuery};
415
416 #[async_std::test]
417 async fn permissions() {
418 database_test!(|db| async move {
419 fixture!(db, "server_with_roles",
420 owner user 0
421 moderator user 1
422 user user 2
423 server server 4);
424
425 let mut query = DatabasePermissionQuery::new(&db, &owner).server(&server);
426 assert!(calculate_server_permissions(&mut query)
427 .await
428 .has_channel_permission(ChannelPermission::GrantAllSafe));
429
430 let mut query = DatabasePermissionQuery::new(&db, &moderator).server(&server);
431 assert!(calculate_server_permissions(&mut query)
432 .await
433 .has_channel_permission(ChannelPermission::BanMembers));
434
435 let mut query = DatabasePermissionQuery::new(&db, &user).server(&server);
436 assert!(!calculate_server_permissions(&mut query)
437 .await
438 .has_channel_permission(ChannelPermission::BanMembers));
439 });
440 }
441}