mirror of
https://github.com/modrinth/code.git
synced 2026-08-28 02:24:56 +00:00
* feat(labrinth): project disclosures model * feat(labrinth): project disclosures database model * feat(labrinth): project disclosures get endpoint * feat(labrinth): censor user ids if set by moderator * feat(labrinth): wrap disclosures in struct * feat(labrinth): edit project disclosures endpoint * style(labrinth): cargo fmt * style(labrinth): fix typo * feat(labrinth): add fields for ai content disclosure * fix(labrinth): field typo * chore(labrinth): update query cache * feat(labrinth): index disclosures in search * refactor(labrinth): use enum instead of bools * feat(labrinth): trigger incremental index on disclosure change * feat(labrinth): change archived status to disclosure * feat(labrinth): use disclosure for archival status * fix(labrinth): error type for deserialization * fix(labrinth): migration timestamp * fix(labrinth): add disclosures to elasticsearch schema * fix(labrinth): don't remove archival disclosure when changing status via v3 api * feat(labrinth): derive str for ai usages and telemtry consent * feat(labrinth): include ai usages and telemetry consent in disclosure types * refactor(labrinth): move disclosure parsing to document creation * fix(labrinth): prevent removing moderator added archival disclosures * fix(labrinth): dedupe ai usages * fix(labrinth): auth logic on archived disclosure removal * feat(labrinth): add interactions field * style(labrinth): cargo fmt * feat(labrinth): soft delete disclosures * feat(labrinth): return soft-deleted disclosures * fix(labrinth): update disclosure on removal --------- Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
146 lines
3.7 KiB
Rust
146 lines
3.7 KiB
Rust
use crate::database::models::DBProjectDisclosure;
|
|
use ariadne::ids::UserId;
|
|
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::BTreeSet;
|
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
|
|
use utoipa::ToSchema;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, EnumDiscriminants)]
|
|
#[serde(rename_all = "snake_case", tag = "type")]
|
|
#[strum_discriminants(
|
|
name(ProjectDisclosureType),
|
|
derive(IntoStaticStr, EnumString),
|
|
strum(serialize_all = "snake_case")
|
|
)]
|
|
pub enum ProjectDisclosure {
|
|
AiContent {
|
|
note: Option<String>,
|
|
uses: BTreeSet<AiUsages>,
|
|
},
|
|
Advertisements {
|
|
note: Option<String>,
|
|
},
|
|
EpilepsyTriggers {
|
|
note: Option<String>,
|
|
},
|
|
SystemInteractions {
|
|
note: Option<String>,
|
|
interactions: Vec<String>,
|
|
},
|
|
Telemetry {
|
|
consent: TelemetryConsent,
|
|
data_collected: Vec<String>,
|
|
},
|
|
DerivativeWork {
|
|
sources: Vec<DerivativeSource>,
|
|
},
|
|
PaidFeatures {
|
|
features: Vec<String>,
|
|
},
|
|
Archived {
|
|
note: Option<String>,
|
|
},
|
|
}
|
|
|
|
impl ProjectDisclosure {
|
|
pub fn to_parts(
|
|
&self,
|
|
) -> Result<(&'static str, serde_json::Value), serde_json::Error> {
|
|
let serde_json::Value::Object(mut object) = serde_json::to_value(self)?
|
|
else {
|
|
return Err(serde::ser::Error::custom(
|
|
"project disclosure must serialize to a JSON object",
|
|
));
|
|
};
|
|
object.remove("type");
|
|
Ok((
|
|
ProjectDisclosureType::from(self).into(),
|
|
serde_json::Value::Object(object),
|
|
))
|
|
}
|
|
|
|
pub fn from_parts(
|
|
kind: &str,
|
|
metadata: serde_json::Value,
|
|
) -> Result<Self, serde_json::Error> {
|
|
let serde_json::Value::Object(mut object) = metadata else {
|
|
return Err(serde::de::Error::custom(
|
|
"project disclosure metadata must be a JSON object",
|
|
));
|
|
};
|
|
object.insert(
|
|
"type".to_owned(),
|
|
serde_json::Value::String(kind.to_owned()),
|
|
);
|
|
serde_json::from_value(serde_json::Value::Object(object))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct ProjectDisclosureData {
|
|
#[serde(flatten)]
|
|
pub disclosure: ProjectDisclosure,
|
|
pub set_by_moderator: bool,
|
|
pub updated_at: DateTime<Utc>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub updated_by: Option<UserId>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub deleted_at: Option<DateTime<Utc>>,
|
|
}
|
|
|
|
impl ProjectDisclosureData {
|
|
pub fn from_db(
|
|
value: DBProjectDisclosure,
|
|
viewer_is_moderator: bool,
|
|
) -> Self {
|
|
let updated_by = (!value.set_by_moderator || viewer_is_moderator)
|
|
.then_some(value.updated_by.into());
|
|
|
|
Self {
|
|
disclosure: value.disclosure,
|
|
set_by_moderator: value.set_by_moderator,
|
|
updated_at: value.updated_at,
|
|
updated_by,
|
|
deleted_at: value.deleted_at,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(
|
|
Debug,
|
|
Clone,
|
|
Serialize,
|
|
Deserialize,
|
|
ToSchema,
|
|
IntoStaticStr,
|
|
PartialEq,
|
|
Eq,
|
|
PartialOrd,
|
|
Ord,
|
|
)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[strum(serialize_all = "snake_case")]
|
|
pub enum AiUsages {
|
|
Code,
|
|
Assets,
|
|
Text,
|
|
Functionality,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, IntoStaticStr)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[strum(serialize_all = "snake_case")]
|
|
pub enum TelemetryConsent {
|
|
OptIn,
|
|
OptOut,
|
|
AlwaysActive,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct DerivativeSource {
|
|
pub link: Option<String>,
|
|
pub label: String,
|
|
pub note: Option<String>,
|
|
}
|