feat(core/database): implement server model
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -2862,6 +2862,7 @@ dependencies = [
|
|||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"ulid 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4234,6 +4235,15 @@ dependencies = [
|
|||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ulid"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13a3aaa69b04e5b66cc27309710a569ea23593612387d67daaf102e73aa974fd"
|
||||||
|
dependencies = [
|
||||||
|
"rand 0.8.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uname"
|
name = "uname"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ default = [ "mongodb", "async-std-runtime" ]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-result = { version = "0.0.2", path = "../result" }
|
revolt-result = { version = "0.0.2", path = "../result" }
|
||||||
revolt-permissions = { version = "0.0.2", path = "../permissions" }
|
revolt-permissions = { version = "0.0.2", path = "../permissions", features = [ "serde" ] }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
ulid = "1.0.0"
|
||||||
nanoid = "0.4.0"
|
nanoid = "0.4.0"
|
||||||
once_cell = "1.17"
|
once_cell = "1.17"
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc};
|
|||||||
|
|
||||||
use futures::lock::Mutex;
|
use futures::lock::Mutex;
|
||||||
|
|
||||||
use crate::{Bot, File, User, UserSettings};
|
use crate::{Bot, File, Server, User, UserSettings};
|
||||||
|
|
||||||
database_derived!(
|
database_derived!(
|
||||||
/// Reference implementation
|
/// Reference implementation
|
||||||
@@ -11,9 +11,9 @@ database_derived!(
|
|||||||
pub bots: Arc<Mutex<HashMap<String, Bot>>>,
|
pub bots: Arc<Mutex<HashMap<String, Bot>>>,
|
||||||
pub user_settings: Arc<Mutex<HashMap<String, UserSettings>>>,
|
pub user_settings: Arc<Mutex<HashMap<String, UserSettings>>>,
|
||||||
pub users: Arc<Mutex<HashMap<String, User>>>,
|
pub users: Arc<Mutex<HashMap<String, User>>>,
|
||||||
|
pub servers: Arc<Mutex<HashMap<String, Server>>>,
|
||||||
pub files: Arc<Mutex<HashMap<String, File>>>,
|
pub files: Arc<Mutex<HashMap<String, File>>>,
|
||||||
|
|
||||||
pub servers: Arc<Mutex<HashMap<String, ()>>>,
|
|
||||||
pub server_bans: Arc<Mutex<HashMap<String, ()>>>,
|
pub server_bans: Arc<Mutex<HashMap<String, ()>>>,
|
||||||
pub server_members: Arc<Mutex<HashMap<String, ()>>>,
|
pub server_members: Arc<Mutex<HashMap<String, ()>>>,
|
||||||
pub safety_reports: Arc<Mutex<HashMap<String, ()>>>,
|
pub safety_reports: Arc<Mutex<HashMap<String, ()>>>,
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
mod admin_migrations;
|
mod admin_migrations;
|
||||||
mod bots;
|
mod bots;
|
||||||
mod files;
|
mod files;
|
||||||
|
mod servers;
|
||||||
mod user_settings;
|
mod user_settings;
|
||||||
mod users;
|
mod users;
|
||||||
|
|
||||||
pub use admin_migrations::*;
|
pub use admin_migrations::*;
|
||||||
pub use bots::*;
|
pub use bots::*;
|
||||||
pub use files::*;
|
pub use files::*;
|
||||||
|
pub use servers::*;
|
||||||
pub use user_settings::*;
|
pub use user_settings::*;
|
||||||
pub use users::*;
|
pub use users::*;
|
||||||
|
|
||||||
@@ -18,6 +20,7 @@ pub trait AbstractDatabase:
|
|||||||
+ admin_migrations::AbstractMigrations
|
+ admin_migrations::AbstractMigrations
|
||||||
+ bots::AbstractBots
|
+ bots::AbstractBots
|
||||||
+ files::AbstractAttachments
|
+ files::AbstractAttachments
|
||||||
|
+ servers::AbstractServers
|
||||||
+ user_settings::AbstractUserSettings
|
+ user_settings::AbstractUserSettings
|
||||||
+ users::AbstractUsers
|
+ 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,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| create_database_error!("find", "user"))?
|
.map_err(|_| create_database_error!("find", "users"))?
|
||||||
.filter_map(|s| async {
|
.filter_map(|s| async {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
Some(s.unwrap())
|
Some(s.unwrap())
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/// Representation of a single permission override
|
/// Representation of a single permission override
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct Override {
|
pub struct Override {
|
||||||
/// Allow bit flags
|
/// Allow bit flags
|
||||||
@@ -10,7 +10,7 @@ pub struct Override {
|
|||||||
|
|
||||||
/// Representation of a single permission override
|
/// Representation of a single permission override
|
||||||
/// as it appears on models and in the database
|
/// as it appears on models and in the database
|
||||||
#[derive(/*JsonSchema, */ Debug, Clone, Copy, Default)]
|
#[derive(/*JsonSchema, */ Debug, Clone, Copy, Default, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct OverrideField {
|
pub struct OverrideField {
|
||||||
/// Allow bit flags
|
/// Allow bit flags
|
||||||
|
|||||||
Reference in New Issue
Block a user