mirror of
https://github.com/modrinth/code.git
synced 2026-08-28 02:24:56 +00:00
Add moderator notes to users & organizations (#6094)
* Moderator notes
* Use macros
* Improve queries
* Query cache
* Accept missing If-Match if no existing note
* Undo v2 compat changes
* Fix tests
* Remove CONSTRAINT CHECK on moderation_notes
* Respect 1-indexing on moderation_notes.version default in DB migration
* Remove double Option
* .body("") -> .finish()
* .remove() -> .get().clone()
* cloned
* Review comments
* moderation_notes everywhere
This commit is contained in:
@@ -8,6 +8,7 @@ pub use v3::billing;
|
||||
pub use v3::collections;
|
||||
pub use v3::ids;
|
||||
pub use v3::images;
|
||||
pub use v3::moderation_notes;
|
||||
pub use v3::notifications;
|
||||
pub use v3::oauth_clients;
|
||||
pub use v3::organizations;
|
||||
|
||||
@@ -4,6 +4,7 @@ pub mod billing;
|
||||
pub mod collections;
|
||||
pub mod ids;
|
||||
pub mod images;
|
||||
pub mod moderation_notes;
|
||||
pub mod notifications;
|
||||
pub mod oauth_clients;
|
||||
pub mod organizations;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
use actix_web::{HttpRequest, http::header::IF_MATCH};
|
||||
use ariadne::ids::UserId;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::routes::ApiError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ModerationNote {
|
||||
pub notes: String,
|
||||
pub last_modified: DateTime<Utc>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub last_author: UserId,
|
||||
pub user_rating: i32,
|
||||
pub version: i32,
|
||||
}
|
||||
|
||||
impl From<crate::database::models::DBModerationNote> for ModerationNote {
|
||||
fn from(note: crate::database::models::DBModerationNote) -> Self {
|
||||
Self {
|
||||
notes: note.notes,
|
||||
last_modified: note.last_modified,
|
||||
created_at: note.created_at,
|
||||
last_author: note.last_author.into(),
|
||||
user_rating: note.user_rating,
|
||||
version: note.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PatchModerationNote {
|
||||
pub notes: Option<String>,
|
||||
pub user_rating: Option<i32>,
|
||||
}
|
||||
|
||||
impl PatchModerationNote {
|
||||
pub fn validate_not_empty(&self) -> Result<(), ApiError> {
|
||||
if self.notes.is_none() && self.user_rating.is_none() {
|
||||
return Err(ApiError::InvalidInput(
|
||||
"must specify `notes` or `user_rating`".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_if_match_header(
|
||||
req: &HttpRequest,
|
||||
) -> Result<Option<i32>, ApiError> {
|
||||
let Some(value) = req.headers().get(IF_MATCH) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let value = value.to_str().map_err(|_| {
|
||||
ApiError::InvalidInput(
|
||||
"`if-match` header must be a valid integer".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Some(value.parse::<i32>().map_err(|_| {
|
||||
ApiError::InvalidInput(
|
||||
"`if-match` header must be a valid integer".to_string(),
|
||||
)
|
||||
}))
|
||||
.transpose()
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use super::moderation_notes::ModerationNote;
|
||||
use super::teams::TeamMember;
|
||||
use crate::models::ids::{OrganizationId, TeamId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -23,6 +24,8 @@ pub struct Organization {
|
||||
|
||||
/// A list of the members of the organization
|
||||
pub members: Vec<TeamMember>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub moderation_notes: Option<Option<ModerationNote>>,
|
||||
}
|
||||
|
||||
impl Organization {
|
||||
@@ -39,6 +42,7 @@ impl Organization {
|
||||
members: team_members,
|
||||
icon_url: data.icon_url,
|
||||
color: data.color,
|
||||
moderation_notes: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use super::moderation_notes::ModerationNote;
|
||||
use crate::{auth::AuthProvider, bitflags_serde_impl};
|
||||
use ariadne::ids::UserId;
|
||||
pub use ariadne::users::UserStatus;
|
||||
@@ -64,6 +65,8 @@ pub struct User {
|
||||
pub payout_data: Option<UserPayoutData>,
|
||||
pub stripe_customer_id: Option<String>,
|
||||
pub allow_friend_requests: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub moderation_notes: Option<Option<ModerationNote>>,
|
||||
|
||||
// DEPRECATED. Always returns None
|
||||
pub github_id: Option<u64>,
|
||||
@@ -98,6 +101,7 @@ impl From<DBUser> for User {
|
||||
github_id: None,
|
||||
stripe_customer_id: None,
|
||||
allow_friend_requests: None,
|
||||
moderation_notes: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,6 +154,7 @@ impl User {
|
||||
}),
|
||||
stripe_customer_id: db_user.stripe_customer_id,
|
||||
allow_friend_requests: Some(db_user.allow_friend_requests),
|
||||
moderation_notes: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user