create models

This commit is contained in:
IAmTomahawkx
2025-06-20 16:40:01 -07:00
committed by Angelo
parent 6ecfd14b07
commit 96f046bc76
18 changed files with 499 additions and 28 deletions

View File

@@ -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<String>,
/// The object the action was taken against, if applicable
pub target: Option<String>,
pub target_id: Option<String>,
/// The context of the action, if applicable (eg. search phrases)
pub context: Option<String>,
}
}
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),
}
}
}

View File

@@ -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(),
);
}
}

View File

@@ -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<String> {
let mut resp: Vec<String> = 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
}
}

View File

@@ -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()),

View File

@@ -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),
}
}
}

View File

@@ -8,17 +8,29 @@ use crate::{AdminMachineToken, Database};
impl FromRequestParts<Database> for AdminMachineToken {
type Rejection = Error;
async fn from_request_parts(parts: &mut Parts, _db: &Database) -> Result<AdminMachineToken> {
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<AdminMachineToken> {
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))
}
}

View File

@@ -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<AdminMachineToken> {
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<AdminMachineToken> {
let user = db.admin_user_fetch(user_id).await?;
Ok(AdminMachineToken { on_behalf_of: user })
}
}

View File

@@ -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<Self, Self::Error> {
let user: &Option<AdminMachineToken> = request
.local_cache_async(async {
let config = revolt_config::config().await;
let db = request.rocket().state::<Database>().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<AdminMachineToken> = 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;

View File

@@ -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<AdminUser> {
return db.admin_user_fetch(id).await;
}
pub async fn find_by_email(email: &str, db: &Database) -> Result<AdminUser> {
return db.admin_user_fetch_email(email).await;
}
}

View File

@@ -12,5 +12,7 @@ pub trait AbstractAdminUsers: Sync + Send {
async fn admin_user_fetch(&self, user_id: &str) -> Result<AdminUser>;
async fn admin_user_fetch_email(&self, email: &str) -> Result<AdminUser>;
async fn admin_user_list(&self) -> Result<Vec<AdminUser>>;
}

View File

@@ -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<AdminUser> {
query!(self, find_one, COL, doc! {"email": email})?.ok_or_else(|| create_error!(NotFound))
}
async fn admin_user_list(&self) -> Result<Vec<AdminUser>> {
query!(self, find, COL, doc! {})
}

View File

@@ -36,6 +36,15 @@ impl AbstractAdminUsers for ReferenceDb {
}
}
async fn admin_user_fetch_email(&self, email: &str) -> Result<AdminUser> {
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<Vec<AdminUser>> {
let admin_users = self.admin_users.lock().await;
Ok(admin_users.iter().map(|(_, u)| u).cloned().collect())

View File

@@ -0,0 +1,3 @@
pub fn transform_optional_string(s: Option<&str>) -> Option<String> {
s.map(|f| f.to_string())
}

View File

@@ -1,3 +1,4 @@
pub mod basic;
pub mod bridge;
pub mod bulk_permissions;
pub mod idempotency;