From 96f046bc76ebb43d54cf455e490caf2f31e69e49 Mon Sep 17 00:00:00 2001 From: IAmTomahawkx Date: Fri, 20 Jun 2025 16:40:01 -0700 Subject: [PATCH] create models --- .../src/models/admin_audits/models.rs | 24 +- .../src/models/admin_case_comments/models.rs | 38 +++ .../database/src/models/admin_cases/models.rs | 32 +++ .../database/src/models/admin_notes/models.rs | 10 + .../src/models/admin_strikes/models.rs | 25 ++ .../database/src/models/admin_tokens/axum.rs | 32 ++- .../src/models/admin_tokens/models.rs | 18 +- .../src/models/admin_tokens/rocket.rs | 35 ++- .../database/src/models/admin_users/models.rs | 27 ++ .../database/src/models/admin_users/ops.rs | 2 + .../src/models/admin_users/ops/mongodb.rs | 4 + .../src/models/admin_users/ops/reference.rs | 9 + crates/core/database/src/util/basic.rs | 3 + crates/core/database/src/util/mod.rs | 1 + crates/core/models/src/v0/admin.rs | 261 ++++++++++++++++++ crates/core/models/src/v0/mod.rs | 2 + crates/core/models/src/v0/safety_reports.rs | 2 + crates/core/models/src/v0/users.rs | 2 + 18 files changed, 499 insertions(+), 28 deletions(-) create mode 100644 crates/core/database/src/util/basic.rs create mode 100644 crates/core/models/src/v0/admin.rs diff --git a/crates/core/database/src/models/admin_audits/models.rs b/crates/core/database/src/models/admin_audits/models.rs index 8a7b2d35..67f5045a 100644 --- a/crates/core/database/src/models/admin_audits/models.rs +++ b/crates/core/database/src/models/admin_audits/models.rs @@ -1,3 +1,5 @@ +use crate::util::basic::transform_optional_string; + auto_derived! { pub struct AdminAuditItem { /// The audit item ID @@ -10,8 +12,28 @@ auto_derived! { /// The relevant case ID, if applicable pub case_id: Option, /// The object the action was taken against, if applicable - pub target: Option, + pub target_id: Option, /// The context of the action, if applicable (eg. search phrases) pub context: Option, } } + +impl AdminAuditItem { + pub fn new( + mod_id: &str, + action: &str, + case_id: Option<&str>, + target_id: Option<&str>, + context: Option<&str>, + ) -> AdminAuditItem { + let id = ulid::Ulid::new().to_string(); + AdminAuditItem { + id, + mod_id: mod_id.to_string(), + action: action.to_string(), + case_id: transform_optional_string(case_id), + target_id: transform_optional_string(target_id), + context: transform_optional_string(context), + } + } +} diff --git a/crates/core/database/src/models/admin_case_comments/models.rs b/crates/core/database/src/models/admin_case_comments/models.rs index 8a54c2a7..0429d9ce 100644 --- a/crates/core/database/src/models/admin_case_comments/models.rs +++ b/crates/core/database/src/models/admin_case_comments/models.rs @@ -14,3 +14,41 @@ auto_derived_partial! { }, "PartialAdminCaseComment" } + +impl AdminCaseComment { + pub fn new(case_id: &str, user_id: &str, content: &str) -> AdminCaseComment { + let id = ulid::Ulid::new().to_string(); + AdminCaseComment { + id, + case_id: case_id.to_string(), + user_id: user_id.to_string(), + edited_at: None, + content: content.to_string(), + } + } + + /// Edit the comment, updating the edited_at time as well + pub fn edit(&mut self, content: &str) { + self.content = content.to_string(); + self.edited_at = Some( + iso8601_timestamp::Timestamp::now_utc() + .format_short() + .to_string(), + ); + } +} + +impl PartialAdminCaseComment { + pub fn new() -> PartialAdminCaseComment { + PartialAdminCaseComment::default() + } + /// Edit the comment, updating the edited_at time as well + pub fn edit(&mut self, content: &str) { + self.content = Some(content.to_string()); + self.edited_at = Some( + iso8601_timestamp::Timestamp::now_utc() + .format_short() + .to_string(), + ); + } +} diff --git a/crates/core/database/src/models/admin_cases/models.rs b/crates/core/database/src/models/admin_cases/models.rs index 0764b139..ac07a6ac 100644 --- a/crates/core/database/src/models/admin_cases/models.rs +++ b/crates/core/database/src/models/admin_cases/models.rs @@ -19,3 +19,35 @@ auto_derived_partial! { }, "PartialAdminCase" } + +impl AdminCase { + pub fn new(owner_id: &str, title: &str) -> AdminCase { + let id = ulid::Ulid::new().to_string(); + let short_id = id.clone().split_off(id.len() - 7); + + AdminCase { + id, + short_id, + owner_id: owner_id.to_string(), + title: title.to_string(), + status: "Open".to_string(), + closed_at: None, + tags: vec![], + } + } + + pub fn merge_tags(&self, other: &[String]) -> Vec { + let mut resp: Vec = vec![]; + // Shitty combining chain; itll only ever be like 5 items + resp.extend(self.tags.clone()); + resp.extend(other.iter().filter_map(|p| { + if !self.tags.contains(p) { + Some(p.clone()) + } else { + None + } + })); + + resp + } +} diff --git a/crates/core/database/src/models/admin_notes/models.rs b/crates/core/database/src/models/admin_notes/models.rs index ff3767c0..f4be8229 100644 --- a/crates/core/database/src/models/admin_notes/models.rs +++ b/crates/core/database/src/models/admin_notes/models.rs @@ -14,6 +14,16 @@ auto_derived_partial! { } impl AdminObjectNote { + pub fn new(object_id: &str, user_id: &str, content: &str) -> AdminObjectNote { + AdminObjectNote { + id: object_id.to_string(), + edited_at: iso8601_timestamp::Timestamp::now_utc() + .format_short() + .to_string(), + last_edited_by_id: user_id.to_string(), + content: content.to_string(), + } + } pub fn to_partial(&self) -> PartialAdminObjectNote { PartialAdminObjectNote { id: Some(self.id.clone()), diff --git a/crates/core/database/src/models/admin_strikes/models.rs b/crates/core/database/src/models/admin_strikes/models.rs index 3d8db55f..def17cdd 100644 --- a/crates/core/database/src/models/admin_strikes/models.rs +++ b/crates/core/database/src/models/admin_strikes/models.rs @@ -1,3 +1,5 @@ +use crate::util::basic::transform_optional_string; + auto_derived_partial! { pub struct AdminStrike { /// The strike ID @@ -20,3 +22,26 @@ auto_derived_partial! { }, "PartialAdminStrike" } + +impl AdminStrike { + pub fn new( + target_id: &str, + mod_id: &str, + case_id: Option<&str>, + associated_action: Option<&str>, + reason: &str, + mod_context: Option<&str>, + ) -> AdminStrike { + let id = ulid::Ulid::new().to_string(); + AdminStrike { + id, + target_id: target_id.to_string(), + mod_id: mod_id.to_string(), + case_id: transform_optional_string(case_id), + associated_action: transform_optional_string(associated_action), + overruled: false, + reason: reason.to_string(), + mod_context: transform_optional_string(mod_context), + } + } +} diff --git a/crates/core/database/src/models/admin_tokens/axum.rs b/crates/core/database/src/models/admin_tokens/axum.rs index cf98fb68..1233ae8f 100644 --- a/crates/core/database/src/models/admin_tokens/axum.rs +++ b/crates/core/database/src/models/admin_tokens/axum.rs @@ -8,17 +8,29 @@ use crate::{AdminMachineToken, Database}; impl FromRequestParts for AdminMachineToken { type Rejection = Error; - async fn from_request_parts(parts: &mut Parts, _db: &Database) -> Result { - if let Some(Ok(token)) = parts.headers.get("x-admin-machine").map(|v| v.to_str()) { - let config = revolt_config::config().await; - let token = token.to_string(); - if config.api.security.admin_keys.contains(&token) { - Ok(AdminMachineToken::new()) - } else { - Err(create_error!(InvalidCredentials)) + async fn from_request_parts(parts: &mut Parts, db: &Database) -> Result { + if let Some(Ok(on_behalf_of)) = parts + .headers + .get("x-admin-on-behalf-of") + .map(|v| v.to_str()) + { + if let Some(Ok(token)) = parts.headers.get("x-admin-machine").map(|v| v.to_str()) { + let config = revolt_config::config().await; + let token = token.to_string(); + if config.api.security.admin_keys.contains(&token) { + let resp: AdminMachineToken; + // shitty email check + if on_behalf_of.contains("@") { + resp = AdminMachineToken::new_from_email(on_behalf_of, db).await? + } else { + resp = AdminMachineToken::new_from_id(on_behalf_of, db).await? + } + return Ok(resp); + } else { + return Err(create_error!(InvalidCredentials)); + } } - } else { - Err(create_error!(NotAuthenticated)) } + Err(create_error!(NotAuthenticated)) } } diff --git a/crates/core/database/src/models/admin_tokens/models.rs b/crates/core/database/src/models/admin_tokens/models.rs index ce4c4a7a..0ffe3dd1 100644 --- a/crates/core/database/src/models/admin_tokens/models.rs +++ b/crates/core/database/src/models/admin_tokens/models.rs @@ -1,3 +1,7 @@ +use revolt_result::Result; + +use crate::{AdminUser, Database}; + auto_derived! { pub struct AdminToken { /// The token ID @@ -14,18 +18,18 @@ auto_derived! { /// This struct is used to validate machine tokens when doing machine to machine communication. pub struct AdminMachineToken { /// Placeholder field. - pub valid: bool + pub on_behalf_of: AdminUser } } impl AdminMachineToken { - pub fn new() -> AdminMachineToken { - AdminMachineToken { valid: true } + pub async fn new_from_email(email: &str, db: &Database) -> Result { + let user = db.admin_user_fetch_email(email).await?; + Ok(AdminMachineToken { on_behalf_of: user }) } -} -impl Default for AdminMachineToken { - fn default() -> Self { - AdminMachineToken::new() + pub async fn new_from_id(user_id: &str, db: &Database) -> Result { + let user = db.admin_user_fetch(user_id).await?; + Ok(AdminMachineToken { on_behalf_of: user }) } } diff --git a/crates/core/database/src/models/admin_tokens/rocket.rs b/crates/core/database/src/models/admin_tokens/rocket.rs index 15e6078e..ed5b771b 100644 --- a/crates/core/database/src/models/admin_tokens/rocket.rs +++ b/crates/core/database/src/models/admin_tokens/rocket.rs @@ -1,7 +1,8 @@ +use revolt_result::Result; use rocket::http::Status; use rocket::request::{self, FromRequest, Outcome, Request}; -use crate::AdminMachineToken; +use crate::{AdminMachineToken, Database}; #[rocket::async_trait] impl<'r> FromRequest<'r> for AdminMachineToken { @@ -10,20 +11,34 @@ impl<'r> FromRequest<'r> for AdminMachineToken { async fn from_request(request: &'r Request<'_>) -> request::Outcome { let user: &Option = request .local_cache_async(async { - let config = revolt_config::config().await; + let db = request.rocket().state::().expect("`Database`"); - let token = request + if let Some(on_behalf_of) = request .headers() - .get("x-admin-machine") + .get("x-admin-on-behalf-of") .next() - .map(|x| x.to_string()); - - if let Some(token) = token { - if config.api.security.admin_keys.contains(&token) { - return Some(AdminMachineToken::new()); + .map(|x| x.to_string()) + { + if let Some(token) = request + .headers() + .get("x-admin-machine") + .next() + .map(|x| x.to_string()) + { + let config = revolt_config::config().await; + let token = token.to_string(); + if config.api.security.admin_keys.contains(&token) { + let resp: Result = if on_behalf_of.contains("@") { + AdminMachineToken::new_from_email(&on_behalf_of, db).await + } else { + AdminMachineToken::new_from_id(&on_behalf_of, db).await + }; + if let Ok(resp) = resp { + return Some(resp); + } + } } } - None }) .await; diff --git a/crates/core/database/src/models/admin_users/models.rs b/crates/core/database/src/models/admin_users/models.rs index 2242fe72..a931331b 100644 --- a/crates/core/database/src/models/admin_users/models.rs +++ b/crates/core/database/src/models/admin_users/models.rs @@ -1,8 +1,14 @@ +use revolt_result::Result; + +use crate::Database; + auto_derived_partial! { pub struct AdminUser { /// The ID of the user #[serde(rename = "_id")] pub id: String, + /// The user's revolt ID. + pub platform_user_id: String, /// The user's email pub email: String, /// Whether the user is active or not (ie. can they use the api) @@ -12,3 +18,24 @@ auto_derived_partial! { }, "PartialAdminUser" } + +impl AdminUser { + pub fn new(email: &str, platform_user_id: &str, permissions: u64) -> AdminUser { + let id = ulid::Ulid::new().to_string(); + AdminUser { + id, + platform_user_id: platform_user_id.to_string(), + email: email.to_string(), + active: true, + permissions, + } + } + + pub async fn find_by_id(id: &str, db: &Database) -> Result { + return db.admin_user_fetch(id).await; + } + + pub async fn find_by_email(email: &str, db: &Database) -> Result { + return db.admin_user_fetch_email(email).await; + } +} diff --git a/crates/core/database/src/models/admin_users/ops.rs b/crates/core/database/src/models/admin_users/ops.rs index 664aca4a..044f380d 100644 --- a/crates/core/database/src/models/admin_users/ops.rs +++ b/crates/core/database/src/models/admin_users/ops.rs @@ -12,5 +12,7 @@ pub trait AbstractAdminUsers: Sync + Send { async fn admin_user_fetch(&self, user_id: &str) -> Result; + async fn admin_user_fetch_email(&self, email: &str) -> Result; + async fn admin_user_list(&self) -> Result>; } diff --git a/crates/core/database/src/models/admin_users/ops/mongodb.rs b/crates/core/database/src/models/admin_users/ops/mongodb.rs index 11513db5..2f6fd552 100644 --- a/crates/core/database/src/models/admin_users/ops/mongodb.rs +++ b/crates/core/database/src/models/admin_users/ops/mongodb.rs @@ -21,6 +21,10 @@ impl AbstractAdminUsers for MongoDb { query!(self, find_one_by_id, COL, user_id)?.ok_or_else(|| create_error!(NotFound)) } + async fn admin_user_fetch_email(&self, email: &str) -> Result { + query!(self, find_one, COL, doc! {"email": email})?.ok_or_else(|| create_error!(NotFound)) + } + async fn admin_user_list(&self) -> Result> { query!(self, find, COL, doc! {}) } diff --git a/crates/core/database/src/models/admin_users/ops/reference.rs b/crates/core/database/src/models/admin_users/ops/reference.rs index 5bee2e81..dd75cdb4 100644 --- a/crates/core/database/src/models/admin_users/ops/reference.rs +++ b/crates/core/database/src/models/admin_users/ops/reference.rs @@ -36,6 +36,15 @@ impl AbstractAdminUsers for ReferenceDb { } } + async fn admin_user_fetch_email(&self, email: &str) -> Result { + let admin_users = self.admin_users.lock().await; + if let Some((_, user)) = admin_users.iter().filter(|(_, p)| p.email == email).next() { + Ok(user.clone()) + } else { + Err(create_error!(NotFound)) + } + } + async fn admin_user_list(&self) -> Result> { let admin_users = self.admin_users.lock().await; Ok(admin_users.iter().map(|(_, u)| u).cloned().collect()) diff --git a/crates/core/database/src/util/basic.rs b/crates/core/database/src/util/basic.rs new file mode 100644 index 00000000..07165d2f --- /dev/null +++ b/crates/core/database/src/util/basic.rs @@ -0,0 +1,3 @@ +pub fn transform_optional_string(s: Option<&str>) -> Option { + s.map(|f| f.to_string()) +} diff --git a/crates/core/database/src/util/mod.rs b/crates/core/database/src/util/mod.rs index 1baf7d8b..065151aa 100644 --- a/crates/core/database/src/util/mod.rs +++ b/crates/core/database/src/util/mod.rs @@ -1,3 +1,4 @@ +pub mod basic; pub mod bridge; pub mod bulk_permissions; pub mod idempotency; diff --git a/crates/core/models/src/v0/admin.rs b/crates/core/models/src/v0/admin.rs new file mode 100644 index 00000000..2dd0668c --- /dev/null +++ b/crates/core/models/src/v0/admin.rs @@ -0,0 +1,261 @@ +use iso8601_timestamp::Timestamp; + +use crate::v0::Report; + +auto_derived! { + #[derive(Default)] + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminAuditItemCreate { + /// The ID of the mod who performed the action + #[cfg_attr(feature="serde", serde(rename="mod"))] + pub mod_id: String, + /// The action performed (previously 'permission') + pub action: String, + /// The relevant case ID, if applicable + #[cfg_attr(feature="serde", serde(rename="case"))] + pub case_id: String, + /// The id of the target (any object) the action was taken against, if applicable + #[cfg_attr(feature="serde", serde(rename="target"))] + pub target_id: Option, + /// The context attached to the action, if applicable (eg. search phrases) + pub context: Option, + } + + pub struct AdminAuditItem { + /// The audit item ID + pub id: String, + /// The ID of the mod who performed the action + #[cfg_attr(feature="serde", serde(rename="mod"))] + pub mod_id: String, + /// The action performed (previously 'permission') + pub action: String, + /// The relevant case ID, if applicable + #[cfg_attr(feature="serde", serde(rename="case"))] + pub case_id: String, + /// The id of the target (any object) the action was taken against, if applicable + #[cfg_attr(feature="serde", serde(rename="target"))] + pub target_id: Option, + /// The context attached to the action, if applicable (eg. search phrases) + pub context: Option, + } + + #[derive(Default)] + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminCaseCommentCreate { + /// The ID of the case this comment is attached to + #[cfg_attr(feature="serde", serde(rename="case"))] + pub case_id: String, + /// The content + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 2000)))] + pub content: String + } + + #[derive(Default)] + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminCaseCommentEdit { + /// The new content + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 2000)))] + pub content: String + } + + pub struct AdminCaseComment { + /// The comment ID + pub id: String, + /// The ID of the case this comment is attached to + #[cfg_attr(feature="serde", serde(rename="case"))] + pub case_id: String, + /// The user who posted the comment + #[cfg_attr(feature="serde", serde(rename="user"))] + pub user_id: String, + /// When the comment was edited, if applicable, in iso8601 + pub edited_at: Option, + /// The content + pub content: String + } + + #[derive(Default)] + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminCaseCreate { + /// The owner of the case. Defaults to the creator + #[cfg_attr(feature="serde", serde(rename="owner"))] + pub owner: Option, + /// The title of the case. If not provided, a default will be generated from the report(s) assigned + pub title: Option, + /// The report IDs to initially attach to this case (and generate a default title from) + #[cfg_attr(feature = "validator", validate(length(min = 1)))] + pub initial_reports: Vec + } + + #[derive(Default)] + pub struct AdminCaseEdit { + /// The new owner of the case. + #[cfg_attr(feature="serde", serde(rename="owner"))] + pub owner_id: Option, + /// The new title of the case. + pub title: Option, + /// Report IDs to add to the case. + pub add_reports: Option>, + /// Report IDs to remove from the case. + pub remove_reports: Option> + } + + pub struct AdminCase { + /// The case ID + pub id: String, + /// The case Short ID + pub short_id: String, + /// The owner of the case + #[cfg_attr(feature="serde", serde(rename="owner"))] + pub owner_id: String, + /// The title of the case + pub title: String, + /// The status of the case (open/closed) + pub status: String, + /// When the case was closed + pub closed_at: Option, + /// The tags for the case + pub tags: Vec, + /// The reports assigned to this case + pub reports: Vec + } + + + pub struct AdminObjectNote { + /// When the note was edited, in iso8601 + pub edited_at: Timestamp, + /// The last user to edit the note + #[cfg_attr(feature="serde", serde(rename="last_edited_by"))] + pub last_edited_by_id: String, + /// The content of the note + pub content: String, + } + + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminObjectNoteEdit { + #[cfg_attr(feature = "validator", validate(length(max = 2000)))] + pub content: String + } + + pub struct AdminStrike { + /// The strike ID + pub id: String, + /// The object receiving the strike (user/server) + #[cfg_attr(feature="serde", serde(rename="target"))] + pub target_id: String, + /// The moderator who gave the strike + #[cfg_attr(feature="serde", serde(rename="mod"))] + pub mod_id: String, + /// The case the strike was made under + #[cfg_attr(feature="serde", serde(rename="case"))] + #[cfg_attr(feature="serde", serde(skip_serializing_if="Option::is_none"))] + pub case_id: Option, + /// Action associated with the strike (eg. suspension/ban) + #[cfg_attr(feature="serde", serde(skip_serializing_if="Option::is_none"))] + pub associated_action: Option, + /// Has the strike been removed + #[cfg_attr(feature="serde", serde(skip_serializing_if="crate::if_false"))] + pub overruled: bool, + /// The user-facing reason for the strike + pub reason: String, + /// Internal context for the strike + #[cfg_attr(feature="serde", serde(skip_serializing_if="Option::is_none"))] + pub mod_context: Option, + } + + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminStrikeCreate { + /// The object receiving the strike (user/server) + #[cfg_attr(feature="serde", serde(rename="target"))] + pub target_id: String, + /// The case the strike was made under + #[cfg_attr(feature="serde", serde(rename="case"))] + pub case_id: Option, + /// Action associated with the strike (eg. suspension/ban) + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 25)))] + pub associated_action: Option, + /// The user-facing reason for the strike + #[cfg_attr(feature = "validator", validate(length(max = 2000)))] + pub reason: String, + /// Internal context for the strike + #[cfg_attr(feature = "validator", validate(length(max = 2000)))] + pub mod_context: Option, + } + + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminStrikeEdit { + /// The case the strike was made under + #[cfg_attr(feature="serde", serde(rename="case"))] + pub case_id: Option, + /// Action associated with the strike (eg. suspension/ban) + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 25)))] + pub associated_action: Option, + /// The user-facing reason for the strike + #[cfg_attr(feature = "validator", validate(length(max = 2000)))] + pub reason: Option, + /// Internal context for the strike + #[cfg_attr(feature = "validator", validate(length(max = 2000)))] + pub mod_context: Option, + } + + pub struct AdminToken { + /// The token ID + pub id: String, + /// The user this token is attached to + #[cfg_attr(feature = "serde", serde(rename="user"))] + pub user_id: String, + /// The token itself + pub token: String, + /// The expiry timestamp for this token, in iso6801 + pub expiry: Timestamp + } + + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminTokenCreate { + /// the user who this token is for + #[cfg_attr(feature = "serde", serde(rename="user"))] + pub user_id: String, + /// The expiry timestamp for this token, in iso6801. Max 30 days from current time. + pub expiry: Timestamp, + } + + pub struct AdminUser { + /// The ID of the user + pub id: String, + /// The user's revolt ID. + pub platform_user_id: String, + /// The user's email + pub email: String, + /// Whether the user is active or not (ie. can they use the api) + pub active: bool, + /// The permissions of the user + pub permissions: u64, + } + + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminUserCreate { + /// The user's revolt ID. + #[cfg_attr(feature = "validator", validate(length(min = 10, max = 20)))] + pub platform_user_id: String, + /// The user's email + #[cfg_attr(feature = "validator", validate(email))] + pub email: String, + /// Whether the user is active or not (ie. can they use the api) + pub active: bool, + /// The permissions of the user + pub permissions: u64, + } + + #[cfg_attr(feature = "validator", derive(validator::Validate))] + pub struct AdminUserEdit { + /// The user's revolt ID. + #[cfg_attr(feature = "validator", validate(length(min = 10, max = 20)))] + pub platform_user_id: Option, + /// The user's email + #[cfg_attr(feature = "validator", validate(email))] + pub email: Option, + /// Whether the user is active or not (ie. can they use the api) + pub active: Option, + /// The permissions of the user + pub permissions: Option, + } +} diff --git a/crates/core/models/src/v0/mod.rs b/crates/core/models/src/v0/mod.rs index 0468493b..5a81a23e 100644 --- a/crates/core/models/src/v0/mod.rs +++ b/crates/core/models/src/v0/mod.rs @@ -1,3 +1,4 @@ +mod admin; mod bots; mod channel_invites; mod channel_unreads; @@ -15,6 +16,7 @@ mod servers; mod user_settings; mod users; +pub use admin::*; pub use bots::*; pub use channel_invites::*; pub use channel_unreads::*; diff --git a/crates/core/models/src/v0/safety_reports.rs b/crates/core/models/src/v0/safety_reports.rs index 0d8ee5d7..07579425 100644 --- a/crates/core/models/src/v0/safety_reports.rs +++ b/crates/core/models/src/v0/safety_reports.rs @@ -8,6 +8,8 @@ auto_derived!( pub id: String, /// Id of the user creating this report pub author_id: String, + /// The case this report is attached to + pub case_id: Option, /// Reported content pub content: ReportedContent, /// Additional report context diff --git a/crates/core/models/src/v0/users.rs b/crates/core/models/src/v0/users.rs index fc681e00..ff25f1bd 100644 --- a/crates/core/models/src/v0/users.rs +++ b/crates/core/models/src/v0/users.rs @@ -200,6 +200,8 @@ auto_derived!( Banned = 4, /// User was marked as spam and removed from platform Spam = 8, + /// User is a global moderator + GlobalModerator = 16, } /// New user profile data