mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 13:36:59 +00:00
feat(core/database): implement server model
This commit is contained in:
@@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use futures::lock::Mutex;
|
||||
|
||||
use crate::{Bot, File, User, UserSettings};
|
||||
use crate::{Bot, File, Server, User, UserSettings};
|
||||
|
||||
database_derived!(
|
||||
/// Reference implementation
|
||||
@@ -11,9 +11,9 @@ database_derived!(
|
||||
pub bots: Arc<Mutex<HashMap<String, Bot>>>,
|
||||
pub user_settings: Arc<Mutex<HashMap<String, UserSettings>>>,
|
||||
pub users: Arc<Mutex<HashMap<String, User>>>,
|
||||
pub servers: Arc<Mutex<HashMap<String, Server>>>,
|
||||
pub files: Arc<Mutex<HashMap<String, File>>>,
|
||||
|
||||
pub servers: Arc<Mutex<HashMap<String, ()>>>,
|
||||
pub server_bans: Arc<Mutex<HashMap<String, ()>>>,
|
||||
pub server_members: Arc<Mutex<HashMap<String, ()>>>,
|
||||
pub safety_reports: Arc<Mutex<HashMap<String, ()>>>,
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
mod admin_migrations;
|
||||
mod bots;
|
||||
mod files;
|
||||
mod servers;
|
||||
mod user_settings;
|
||||
mod users;
|
||||
|
||||
pub use admin_migrations::*;
|
||||
pub use bots::*;
|
||||
pub use files::*;
|
||||
pub use servers::*;
|
||||
pub use user_settings::*;
|
||||
pub use users::*;
|
||||
|
||||
@@ -18,6 +20,7 @@ pub trait AbstractDatabase:
|
||||
+ admin_migrations::AbstractMigrations
|
||||
+ bots::AbstractBots
|
||||
+ files::AbstractAttachments
|
||||
+ servers::AbstractServers
|
||||
+ user_settings::AbstractUserSettings
|
||||
+ users::AbstractUsers
|
||||
{
|
||||
|
||||
5
crates/core/database/src/models/servers/mod.rs
Normal file
5
crates/core/database/src/models/servers/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod model;
|
||||
mod ops;
|
||||
|
||||
pub use model::*;
|
||||
pub use ops::*;
|
||||
451
crates/core/database/src/models/servers/model.rs
Normal file
451
crates/core/database/src/models/servers/model.rs
Normal file
@@ -0,0 +1,451 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use revolt_permissions::OverrideField;
|
||||
use revolt_result::Result;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{Database, File};
|
||||
|
||||
auto_derived_partial!(
|
||||
/// Server
|
||||
pub struct Server {
|
||||
/// Unique Id
|
||||
#[serde(rename = "_id")]
|
||||
pub id: String,
|
||||
/// User id of the owner
|
||||
pub owner: String,
|
||||
|
||||
/// Name of the server
|
||||
pub name: String,
|
||||
/// Description for the server
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
|
||||
/// Channels within this server
|
||||
// ! FIXME: this may be redundant
|
||||
pub channels: Vec<String>,
|
||||
/// Categories for this server
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub categories: Option<Vec<Category>>,
|
||||
/// Configuration for sending system event messages
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub system_messages: Option<SystemMessageChannels>,
|
||||
|
||||
/// Roles for this server
|
||||
#[serde(
|
||||
default = "HashMap::<String, Role>::new",
|
||||
skip_serializing_if = "HashMap::<String, Role>::is_empty"
|
||||
)]
|
||||
pub roles: HashMap<String, Role>,
|
||||
/// Default set of server and channel permissions
|
||||
pub default_permissions: i64,
|
||||
|
||||
/// Icon attachment
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub icon: Option<File>,
|
||||
/// Banner attachment
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub banner: Option<File>,
|
||||
|
||||
/// Bitfield of server flags
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub flags: Option<i32>,
|
||||
|
||||
/// Whether this server is flagged as not safe for work
|
||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||
pub nsfw: bool,
|
||||
/// Whether to enable analytics
|
||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||
pub analytics: bool,
|
||||
/// Whether this server should be publicly discoverable
|
||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||
pub discoverable: bool,
|
||||
},
|
||||
"PartialServer"
|
||||
);
|
||||
|
||||
auto_derived_partial!(
|
||||
/// Role
|
||||
pub struct Role {
|
||||
/// Role name
|
||||
pub name: String,
|
||||
/// Permissions available to this role
|
||||
pub permissions: OverrideField,
|
||||
/// Colour used for this role
|
||||
///
|
||||
/// This can be any valid CSS colour
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub colour: Option<String>,
|
||||
/// Whether this role should be shown separately on the member sidebar
|
||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||
pub hoist: bool,
|
||||
/// Ranking of this role
|
||||
#[serde(default)]
|
||||
pub rank: i64,
|
||||
},
|
||||
"PartialRole"
|
||||
);
|
||||
|
||||
auto_derived!(
|
||||
/// Channel category
|
||||
pub struct Category {
|
||||
/// Unique ID for this category
|
||||
pub id: String,
|
||||
/// Title for this category
|
||||
pub title: String,
|
||||
/// Channels in this category
|
||||
pub channels: Vec<String>,
|
||||
}
|
||||
|
||||
/// System message channel assignments
|
||||
pub struct SystemMessageChannels {
|
||||
/// ID of channel to send user join messages in
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub user_joined: Option<String>,
|
||||
/// ID of channel to send user left messages in
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub user_left: Option<String>,
|
||||
/// ID of channel to send user kicked messages in
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub user_kicked: Option<String>,
|
||||
/// ID of channel to send user banned messages in
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub user_banned: Option<String>,
|
||||
}
|
||||
|
||||
/// Optional fields on server object
|
||||
pub enum FieldsServer {
|
||||
Description,
|
||||
Categories,
|
||||
SystemMessages,
|
||||
Icon,
|
||||
Banner,
|
||||
}
|
||||
|
||||
/// Optional fields on server object
|
||||
pub enum FieldsRole {
|
||||
Colour,
|
||||
}
|
||||
);
|
||||
|
||||
impl Server {
|
||||
/// Create a server
|
||||
pub async fn create(&self, db: &Database) -> Result<()> {
|
||||
db.insert_server(self).await
|
||||
}
|
||||
|
||||
/// Update server data
|
||||
pub async fn update<'a>(
|
||||
&mut self,
|
||||
db: &Database,
|
||||
partial: PartialServer,
|
||||
remove: Vec<FieldsServer>,
|
||||
) -> Result<()> {
|
||||
for field in &remove {
|
||||
self.remove_field(field);
|
||||
}
|
||||
|
||||
self.apply_options(partial.clone());
|
||||
|
||||
db.update_server(&self.id, &partial, remove.clone()).await?;
|
||||
|
||||
/* // TODO: EventV1::ServerUpdate {
|
||||
id: self.id.clone(),
|
||||
data: partial,
|
||||
clear: remove,
|
||||
}
|
||||
.p(self.id.clone())
|
||||
.await; */
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a server
|
||||
pub async fn delete(self, db: &Database) -> Result<()> {
|
||||
/* // TODO: EventV1::ServerDelete {
|
||||
id: self.id.clone(),
|
||||
}
|
||||
.p(self.id.clone())
|
||||
.await; */
|
||||
|
||||
db.delete_server(&self.id).await
|
||||
}
|
||||
|
||||
/// Remove a field from Server
|
||||
pub fn remove_field(&mut self, field: &FieldsServer) {
|
||||
match field {
|
||||
FieldsServer::Description => self.description = None,
|
||||
FieldsServer::Categories => self.categories = None,
|
||||
FieldsServer::SystemMessages => self.system_messages = None,
|
||||
FieldsServer::Icon => self.icon = None,
|
||||
FieldsServer::Banner => self.banner = None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set role permission on a server
|
||||
pub async fn set_role_permission(
|
||||
&mut self,
|
||||
db: &Database,
|
||||
role_id: &str,
|
||||
permissions: OverrideField,
|
||||
) -> Result<()> {
|
||||
if let Some(role) = self.roles.get_mut(role_id) {
|
||||
role.update(
|
||||
db,
|
||||
&self.id,
|
||||
role_id,
|
||||
PartialRole {
|
||||
permissions: Some(permissions),
|
||||
..Default::default()
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
/* /// Create a new member in a server
|
||||
pub async fn create_member(
|
||||
&self,
|
||||
db: &Database,
|
||||
user: User,
|
||||
channels: Option<Vec<Channel>>,
|
||||
) -> Result<Vec<Channel>> {
|
||||
if db.fetch_ban(&self.id, &user.id).await.is_ok() {
|
||||
return Err(Error::Banned);
|
||||
}
|
||||
|
||||
let member = Member {
|
||||
id: MemberCompositeKey {
|
||||
server: self.id.clone(),
|
||||
user: user.id.clone(),
|
||||
},
|
||||
joined_at: Timestamp::now_utc(),
|
||||
nickname: None,
|
||||
avatar: None,
|
||||
roles: vec![],
|
||||
timeout: None,
|
||||
};
|
||||
|
||||
db.insert_member(&member).await?;
|
||||
|
||||
let should_fetch = channels.is_none();
|
||||
let mut channels = channels.unwrap_or_default();
|
||||
|
||||
if should_fetch {
|
||||
let perm = perms(&user).server(self).member(&member);
|
||||
let existing_channels = db.fetch_channels(&self.channels).await?;
|
||||
for channel in existing_channels {
|
||||
if perm
|
||||
.clone()
|
||||
.channel(&channel)
|
||||
.has_permission(db, Permission::ViewChannel)
|
||||
.await?
|
||||
{
|
||||
channels.push(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* // TODO: EventV1::ServerMemberJoin {
|
||||
id: self.id.clone(),
|
||||
user: user.id.clone(),
|
||||
}
|
||||
.p(self.id.clone())
|
||||
.await;
|
||||
|
||||
EventV1::ServerCreate {
|
||||
id: self.id.clone(),
|
||||
server: self.clone(),
|
||||
channels: channels.clone(),
|
||||
}
|
||||
.private(user.id.clone())
|
||||
.await; */
|
||||
|
||||
if let Some(id) = self
|
||||
.system_messages
|
||||
.as_ref()
|
||||
.and_then(|x| x.user_joined.as_ref())
|
||||
{
|
||||
SystemMessage::UserJoined {
|
||||
id: user.id.clone(),
|
||||
}
|
||||
.into_message(id.to_string())
|
||||
.create_no_web_push(db, id, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
Ok(channels)
|
||||
}
|
||||
|
||||
/// Remove a member from a server
|
||||
pub async fn remove_member(
|
||||
&self,
|
||||
db: &Database,
|
||||
member: Member,
|
||||
intention: RemovalIntention,
|
||||
silent: bool,
|
||||
) -> Result<()> {
|
||||
db.delete_member(&member.id).await?;
|
||||
|
||||
/* // TODO: EventV1::ServerMemberLeave {
|
||||
id: self.id.to_string(),
|
||||
user: member.id.user.clone(),
|
||||
}
|
||||
.p(member.id.server)
|
||||
.await; */
|
||||
|
||||
if !silent {
|
||||
if let Some(id) = self.system_messages.as_ref().and_then(|x| match intention {
|
||||
RemovalIntention::Leave => x.user_left.as_ref(),
|
||||
RemovalIntention::Kick => x.user_kicked.as_ref(),
|
||||
RemovalIntention::Ban => x.user_banned.as_ref(),
|
||||
}) {
|
||||
match intention {
|
||||
RemovalIntention::Leave => SystemMessage::UserLeft { id: member.id.user },
|
||||
RemovalIntention::Kick => SystemMessage::UserKicked { id: member.id.user },
|
||||
RemovalIntention::Ban => SystemMessage::UserBanned { id: member.id.user },
|
||||
}
|
||||
.into_message(id.to_string())
|
||||
.create_no_web_push(db, id, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create ban
|
||||
pub async fn ban_user(
|
||||
self,
|
||||
db: &Database,
|
||||
id: MemberCompositeKey,
|
||||
reason: Option<String>,
|
||||
) -> Result<ServerBan> {
|
||||
let ban = ServerBan { id, reason };
|
||||
db.insert_ban(&ban).await?;
|
||||
Ok(ban)
|
||||
}
|
||||
|
||||
/// Ban a member from a server
|
||||
pub async fn ban_member(
|
||||
self,
|
||||
db: &Database,
|
||||
member: Member,
|
||||
reason: Option<String>,
|
||||
) -> Result<ServerBan> {
|
||||
self.remove_member(db, member.clone(), RemovalIntention::Ban, false)
|
||||
.await?;
|
||||
|
||||
self.ban_user(db, member.id, reason).await
|
||||
} */
|
||||
}
|
||||
|
||||
impl Role {
|
||||
/// Into optional struct
|
||||
pub fn into_optional(self) -> PartialRole {
|
||||
PartialRole {
|
||||
name: Some(self.name),
|
||||
permissions: Some(self.permissions),
|
||||
colour: self.colour,
|
||||
hoist: Some(self.hoist),
|
||||
rank: Some(self.rank),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a role
|
||||
pub async fn create(&self, db: &Database, server_id: &str) -> Result<String> {
|
||||
let role_id = Ulid::new().to_string();
|
||||
db.insert_role(server_id, &role_id, self).await?;
|
||||
|
||||
/* // TODO: EventV1::ServerRoleUpdate {
|
||||
id: server_id.to_string(),
|
||||
role_id: role_id.to_string(),
|
||||
data: self.clone().into_optional(),
|
||||
clear: vec![],
|
||||
}
|
||||
.p(server_id.to_string())
|
||||
.await; */
|
||||
|
||||
Ok(role_id)
|
||||
}
|
||||
|
||||
/// Update server data
|
||||
pub async fn update<'a>(
|
||||
&mut self,
|
||||
db: &Database,
|
||||
server_id: &str,
|
||||
role_id: &str,
|
||||
partial: PartialRole,
|
||||
remove: Vec<FieldsRole>,
|
||||
) -> Result<()> {
|
||||
for field in &remove {
|
||||
self.remove_field(field);
|
||||
}
|
||||
|
||||
self.apply_options(partial.clone());
|
||||
|
||||
db.update_role(server_id, role_id, &partial, remove.clone())
|
||||
.await?;
|
||||
|
||||
/* // TODO: EventV1::ServerRoleUpdate {
|
||||
id: server_id.to_string(),
|
||||
role_id: role_id.to_string(),
|
||||
data: partial,
|
||||
clear: vec![],
|
||||
}
|
||||
.p(server_id.to_string())
|
||||
.await; */
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a role
|
||||
pub async fn delete(self, db: &Database, server_id: &str, role_id: &str) -> Result<()> {
|
||||
/* // TODO: EventV1::ServerRoleDelete {
|
||||
id: server_id.to_string(),
|
||||
role_id: role_id.to_string(),
|
||||
}
|
||||
.p(server_id.to_string())
|
||||
.await; */
|
||||
|
||||
db.delete_role(server_id, role_id).await
|
||||
}
|
||||
|
||||
/// Remove field from Role
|
||||
pub fn remove_field(&mut self, field: &FieldsRole) {
|
||||
match field {
|
||||
FieldsRole::Colour => self.colour = None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemMessageChannels {
|
||||
pub fn into_channel_ids(self) -> HashSet<String> {
|
||||
let mut ids = HashSet::new();
|
||||
|
||||
if let Some(id) = self.user_joined {
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
if let Some(id) = self.user_left {
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
if let Some(id) = self.user_kicked {
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
if let Some(id) = self.user_banned {
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
ids
|
||||
}
|
||||
}
|
||||
46
crates/core/database/src/models/servers/ops.rs
Normal file
46
crates/core/database/src/models/servers/ops.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::{FieldsRole, FieldsServer, PartialRole, PartialServer, Role, Server};
|
||||
|
||||
mod mongodb;
|
||||
mod reference;
|
||||
|
||||
#[async_trait]
|
||||
pub trait AbstractServers: Sync + Send {
|
||||
/// Insert a new server into database
|
||||
async fn insert_server(&self, server: &Server) -> Result<()>;
|
||||
|
||||
/// Fetch a server by its id
|
||||
async fn fetch_server(&self, id: &str) -> Result<Server>;
|
||||
|
||||
/// Fetch a servers by their ids
|
||||
async fn fetch_servers<'a>(&self, ids: &'a [String]) -> Result<Vec<Server>>;
|
||||
|
||||
/// Update a server with new information
|
||||
async fn update_server(
|
||||
&self,
|
||||
id: &str,
|
||||
partial: &PartialServer,
|
||||
remove: Vec<FieldsServer>,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Delete a server by its id
|
||||
async fn delete_server(&self, id: &str) -> Result<()>;
|
||||
|
||||
/// Insert a new role into server object
|
||||
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()>;
|
||||
|
||||
/// Update an existing role on a server
|
||||
async fn update_role(
|
||||
&self,
|
||||
server_id: &str,
|
||||
role_id: &str,
|
||||
partial: &PartialRole,
|
||||
remove: Vec<FieldsRole>,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Delete a role from a server
|
||||
///
|
||||
/// Also updates channels and members.
|
||||
async fn delete_role(&self, server_id: &str, role_id: &str) -> Result<()>;
|
||||
}
|
||||
183
crates/core/database/src/models/servers/ops/mongodb.rs
Normal file
183
crates/core/database/src/models/servers/ops/mongodb.rs
Normal file
@@ -0,0 +1,183 @@
|
||||
use bson::{to_document, Document};
|
||||
use futures::StreamExt;
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::{FieldsRole, FieldsServer, PartialRole, PartialServer, Role, Server};
|
||||
use crate::{IntoDocumentPath, MongoDb};
|
||||
|
||||
use super::AbstractServers;
|
||||
|
||||
static COL: &str = "servers";
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractServers for MongoDb {
|
||||
/// Insert a new server into database
|
||||
async fn insert_server(&self, server: &Server) -> Result<()> {
|
||||
query!(self, insert_one, COL, &server).map(|_| ())
|
||||
}
|
||||
|
||||
/// Fetch a server by its id
|
||||
async fn fetch_server(&self, id: &str) -> Result<Server> {
|
||||
query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(NotFound))
|
||||
}
|
||||
|
||||
/// Fetch a servers by their ids
|
||||
async fn fetch_servers<'a>(&self, ids: &'a [String]) -> Result<Vec<Server>> {
|
||||
Ok(self
|
||||
.col::<Server>(COL)
|
||||
.find(
|
||||
doc! {
|
||||
"_id": {
|
||||
"$in": ids
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| create_database_error!("find", "servers"))?
|
||||
.filter_map(|s| async {
|
||||
if cfg!(debug_assertions) {
|
||||
Some(s.unwrap())
|
||||
} else {
|
||||
s.ok()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.await)
|
||||
}
|
||||
|
||||
/// Update a server with new information
|
||||
async fn update_server(
|
||||
&self,
|
||||
id: &str,
|
||||
partial: &PartialServer,
|
||||
remove: Vec<FieldsServer>,
|
||||
) -> Result<()> {
|
||||
query!(
|
||||
self,
|
||||
update_one_by_id,
|
||||
COL,
|
||||
id,
|
||||
partial,
|
||||
remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
|
||||
None
|
||||
)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
/// Delete a server by its id
|
||||
async fn delete_server(&self, id: &str) -> Result<()> {
|
||||
query!(self, delete_one_by_id, COL, id).map(|_| ())
|
||||
}
|
||||
|
||||
/// Insert a new role into server object
|
||||
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()> {
|
||||
self.col::<Document>(COL)
|
||||
.update_one(
|
||||
doc! {
|
||||
"_id": server_id
|
||||
},
|
||||
doc! {
|
||||
"$set": {
|
||||
"roles.".to_owned() + role_id: to_document(role)
|
||||
.map_err(|_| create_database_error!("to_document", "role"))?
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| create_database_error!("update_one", "server"))
|
||||
}
|
||||
|
||||
/// Update an existing role on a server
|
||||
async fn update_role(
|
||||
&self,
|
||||
server_id: &str,
|
||||
role_id: &str,
|
||||
partial: &PartialRole,
|
||||
remove: Vec<FieldsRole>,
|
||||
) -> Result<()> {
|
||||
query!(
|
||||
self,
|
||||
update_one_by_id,
|
||||
COL,
|
||||
server_id,
|
||||
partial,
|
||||
remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
|
||||
"roles.".to_owned() + role_id + "."
|
||||
)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
/// Delete a role from a server
|
||||
///
|
||||
/// Also updates channels and members.
|
||||
async fn delete_role(&self, server_id: &str, role_id: &str) -> Result<()> {
|
||||
self.col::<Document>("server_members")
|
||||
.update_many(
|
||||
doc! {
|
||||
"_id.server": server_id
|
||||
},
|
||||
doc! {
|
||||
"$pull": {
|
||||
"roles": &role_id
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| create_database_error!("update_many", "server_members"))?;
|
||||
|
||||
self.col::<Document>("channels")
|
||||
.update_one(
|
||||
doc! {
|
||||
"server": server_id
|
||||
},
|
||||
doc! {
|
||||
"$unset": {
|
||||
"role_permissions.".to_owned() + role_id: 1_i32
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| create_database_error!("update_one", "channels"))?;
|
||||
|
||||
self.col::<Document>("servers")
|
||||
.update_one(
|
||||
doc! {
|
||||
"_id": server_id
|
||||
},
|
||||
doc! {
|
||||
"$unset": {
|
||||
"roles.".to_owned() + role_id: 1_i32
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| create_database_error!("update_one", "servers"))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDocumentPath for FieldsServer {
|
||||
fn as_path(&self) -> Option<&'static str> {
|
||||
Some(match self {
|
||||
FieldsServer::Banner => "banner",
|
||||
FieldsServer::Categories => "categories",
|
||||
FieldsServer::Description => "description",
|
||||
FieldsServer::Icon => "icon",
|
||||
FieldsServer::SystemMessages => "system_messages",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDocumentPath for FieldsRole {
|
||||
fn as_path(&self) -> Option<&'static str> {
|
||||
Some(match self {
|
||||
FieldsRole::Colour => "colour",
|
||||
})
|
||||
}
|
||||
}
|
||||
126
crates/core/database/src/models/servers/ops/reference.rs
Normal file
126
crates/core/database/src/models/servers/ops/reference.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::ReferenceDb;
|
||||
use crate::{FieldsRole, FieldsServer, PartialRole, PartialServer, Role, Server};
|
||||
|
||||
use super::AbstractServers;
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractServers for ReferenceDb {
|
||||
/// Insert a new server into database
|
||||
async fn insert_server(&self, server: &Server) -> Result<()> {
|
||||
let mut servers = self.servers.lock().await;
|
||||
if servers.contains_key(&server.id) {
|
||||
Err(create_database_error!("insert", "server"))
|
||||
} else {
|
||||
servers.insert(server.id.to_string(), server.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch a server by its id
|
||||
async fn fetch_server(&self, id: &str) -> Result<Server> {
|
||||
let servers = self.servers.lock().await;
|
||||
servers
|
||||
.get(id)
|
||||
.cloned()
|
||||
.ok_or_else(|| create_error!(NotFound))
|
||||
}
|
||||
|
||||
/// Fetch a servers by their ids
|
||||
async fn fetch_servers<'a>(&self, ids: &'a [String]) -> Result<Vec<Server>> {
|
||||
let servers = self.servers.lock().await;
|
||||
ids.iter()
|
||||
.map(|id| {
|
||||
servers
|
||||
.get(id)
|
||||
.cloned()
|
||||
.ok_or_else(|| create_error!(NotFound))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Update a server with new information
|
||||
async fn update_server(
|
||||
&self,
|
||||
id: &str,
|
||||
partial: &PartialServer,
|
||||
remove: Vec<FieldsServer>,
|
||||
) -> Result<()> {
|
||||
let mut servers = self.servers.lock().await;
|
||||
if let Some(server) = servers.get_mut(id) {
|
||||
for field in remove {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
server.remove_field(&field);
|
||||
}
|
||||
|
||||
server.apply_options(partial.clone());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a server by its id
|
||||
async fn delete_server(&self, id: &str) -> Result<()> {
|
||||
let mut servers = self.servers.lock().await;
|
||||
if servers.remove(id).is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a new role into server object
|
||||
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()> {
|
||||
let mut servers = self.servers.lock().await;
|
||||
if let Some(server) = servers.get_mut(server_id) {
|
||||
server.roles.insert(role_id.to_string(), role.clone());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
/// Update an existing role on a server
|
||||
async fn update_role(
|
||||
&self,
|
||||
server_id: &str,
|
||||
role_id: &str,
|
||||
partial: &PartialRole,
|
||||
remove: Vec<FieldsRole>,
|
||||
) -> Result<()> {
|
||||
let mut servers = self.servers.lock().await;
|
||||
if let Some(server) = servers.get_mut(server_id) {
|
||||
if let Some(role) = server.roles.get_mut(role_id) {
|
||||
for field in remove {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
role.remove_field(&field);
|
||||
}
|
||||
|
||||
role.apply_options(partial.clone());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a role from a server
|
||||
///
|
||||
/// Also updates channels and members.
|
||||
async fn delete_role(&self, server_id: &str, role_id: &str) -> Result<()> {
|
||||
let mut servers = self.servers.lock().await;
|
||||
if let Some(server) = servers.get_mut(server_id) {
|
||||
if server.roles.remove(role_id).is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ impl AbstractUsers for MongoDb {
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| create_database_error!("find", "user"))?
|
||||
.map_err(|_| create_database_error!("find", "users"))?
|
||||
.filter_map(|s| async {
|
||||
if cfg!(debug_assertions) {
|
||||
Some(s.unwrap())
|
||||
|
||||
Reference in New Issue
Block a user