forked from jmug/stoatchat
feat(safety): add API route for editing report
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -2170,7 +2170,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "optional_struct"
|
name = "optional_struct"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/insertish/OptionalStruct?rev=e275d2726595474632485934aa0887fa52281f70#e275d2726595474632485934aa0887fa52281f70"
|
source = "git+https://github.com/insertish/OptionalStruct?rev=ee56427cee1f007839825d93d07fffd5a5e038c7#ee56427cee1f007839825d93d07fffd5a5e038c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 0.3.15",
|
"quote 0.3.15",
|
||||||
"syn 0.11.11",
|
"syn 0.11.11",
|
||||||
@@ -2792,7 +2792,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.5.10"
|
version = "0.5.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
@@ -2808,7 +2808,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.5.10"
|
version = "0.5.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -2848,7 +2848,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.5.10"
|
version = "0.5.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.5.10"
|
version = "0.5.11"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.5.10"
|
version = "0.5.11"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|||||||
56
crates/delta/src/routes/safety/edit_report.rs
Normal file
56
crates/delta/src/routes/safety/edit_report.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use revolt_quark::{
|
||||||
|
models::{
|
||||||
|
report::{PartialReport, ReportStatus},
|
||||||
|
Report, User,
|
||||||
|
},
|
||||||
|
Db, Error, Ref, Result,
|
||||||
|
};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
/// # Report Data
|
||||||
|
#[derive(Validate, Deserialize, JsonSchema)]
|
||||||
|
pub struct DataEditReport {
|
||||||
|
/// New report status
|
||||||
|
status: Option<ReportStatus>,
|
||||||
|
/// Report notes
|
||||||
|
notes: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Edit Report
|
||||||
|
///
|
||||||
|
/// Edit a report.
|
||||||
|
#[openapi(tag = "User Safety")]
|
||||||
|
#[patch("/reports/<report>", data = "<edit>")]
|
||||||
|
pub async fn edit_report(
|
||||||
|
db: &Db,
|
||||||
|
user: User,
|
||||||
|
report: Ref,
|
||||||
|
edit: Json<DataEditReport>,
|
||||||
|
) -> Result<Json<Report>> {
|
||||||
|
// Must be privileged for this route
|
||||||
|
if !user.privileged {
|
||||||
|
return Err(Error::NotPrivileged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate data
|
||||||
|
let edit = edit.into_inner();
|
||||||
|
edit.validate()
|
||||||
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
// Create and apply update to report
|
||||||
|
let mut report = report.as_report(db).await?;
|
||||||
|
report
|
||||||
|
.update(
|
||||||
|
db,
|
||||||
|
PartialReport {
|
||||||
|
status: edit.status,
|
||||||
|
notes: edit.notes,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(report))
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
|
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
|
||||||
use rocket::Route;
|
use rocket::Route;
|
||||||
|
|
||||||
|
mod edit_report;
|
||||||
mod fetch_reports;
|
mod fetch_reports;
|
||||||
mod report_content;
|
mod report_content;
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
|||||||
// Reports
|
// Reports
|
||||||
report_content::report_content,
|
report_content::report_content,
|
||||||
fetch_reports::fetch_reports,
|
fetch_reports::fetch_reports,
|
||||||
|
edit_report::edit_report,
|
||||||
// Snapshots
|
// Snapshots
|
||||||
fetch_snapshot::fetch_snapshot
|
fetch_snapshot::fetch_snapshot
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -149,7 +149,8 @@ pub async fn report_content(db: &Db, user: User, data: Json<DataReportContent>)
|
|||||||
author_id: user.id,
|
author_id: user.id,
|
||||||
content: data.content,
|
content: data.content,
|
||||||
additional_context: data.additional_context,
|
additional_context: data.additional_context,
|
||||||
status: ReportStatus::Created,
|
status: ReportStatus::Created {},
|
||||||
|
notes: String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
db.insert_report(&report).await?;
|
db.insert_report(&report).await?;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.5.10"
|
version = "0.5.11"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ default = [ "test" ]
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
validator = { version = "0.14", features = ["derive"] }
|
validator = { version = "0.14", features = ["derive"] }
|
||||||
iso8601-timestamp = { version = "0.1.8", features = ["schema", "bson"] }
|
iso8601-timestamp = { version = "0.1.8", features = ["schema", "bson"] }
|
||||||
optional_struct = { git = "https://github.com/insertish/OptionalStruct", rev = "e275d2726595474632485934aa0887fa52281f70" }
|
optional_struct = { git = "https://github.com/insertish/OptionalStruct", rev = "ee56427cee1f007839825d93d07fffd5a5e038c7" }
|
||||||
|
|
||||||
# Formats
|
# Formats
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::models::report::PartialReport;
|
||||||
use crate::models::Report;
|
use crate::models::Report;
|
||||||
use crate::{AbstractReport, Result};
|
use crate::{AbstractReport, Result};
|
||||||
|
|
||||||
@@ -10,6 +11,14 @@ impl AbstractReport for DummyDb {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_report(&self, _id: &str, _report: &PartialReport) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_report(&self, _report_id: &str) -> Result<Report> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
async fn fetch_reports(&self) -> Result<Vec<Report>> {
|
async fn fetch_reports(&self) -> Result<Vec<Report>> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,3 +27,7 @@ pub mod users {
|
|||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod user_settings;
|
pub mod user_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod safety {
|
||||||
|
pub mod report;
|
||||||
|
}
|
||||||
|
|||||||
9
crates/quark/src/impl/generic/safety/report.rs
Normal file
9
crates/quark/src/impl/generic/safety/report.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use crate::{models::report::PartialReport, models::Report, Database, Result};
|
||||||
|
|
||||||
|
impl Report {
|
||||||
|
/// Update report data
|
||||||
|
pub async fn update(&mut self, db: &Database, partial: PartialReport) -> Result<()> {
|
||||||
|
self.apply_options(partial.clone());
|
||||||
|
db.update_report(&self.id, &partial).await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,7 +97,14 @@ impl MongoDb {
|
|||||||
operation: "find",
|
operation: "find",
|
||||||
with: collection,
|
with: collection,
|
||||||
})?
|
})?
|
||||||
.filter_map(|s| async { s.ok() })
|
.filter_map(|s| async {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
// Hard fail on invalid documents
|
||||||
|
Some(s.unwrap())
|
||||||
|
} else {
|
||||||
|
s.ok()
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect::<Vec<T>>()
|
.collect::<Vec<T>>()
|
||||||
.await)
|
.await)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::models::report::PartialReport;
|
||||||
use crate::models::Report;
|
use crate::models::Report;
|
||||||
use crate::{AbstractReport, Result};
|
use crate::{AbstractReport, Result};
|
||||||
|
|
||||||
@@ -11,6 +12,16 @@ impl AbstractReport for MongoDb {
|
|||||||
self.insert_one(COL, report).await.map(|_| ())
|
self.insert_one(COL, report).await.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_report(&self, id: &str, report: &PartialReport) -> Result<()> {
|
||||||
|
self.update_one_by_id(COL, id, report, vec![], None)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_report(&self, report_id: &str) -> Result<Report> {
|
||||||
|
self.find_one_by_id(COL, report_id).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn fetch_reports(&self) -> Result<Vec<Report>> {
|
async fn fetch_reports(&self) -> Result<Vec<Report>> {
|
||||||
self.find(COL, doc! {}).await
|
self.find(COL, doc! {}).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,17 +76,20 @@ pub enum ReportedContent {
|
|||||||
#[serde(tag = "status")]
|
#[serde(tag = "status")]
|
||||||
pub enum ReportStatus {
|
pub enum ReportStatus {
|
||||||
/// Report is waiting for triage / action
|
/// Report is waiting for triage / action
|
||||||
Created,
|
Created {},
|
||||||
|
|
||||||
/// Report was rejected
|
/// Report was rejected
|
||||||
Rejected { rejection_reason: String },
|
Rejected { rejection_reason: String },
|
||||||
|
|
||||||
/// Report was actioned and resolved
|
/// Report was actioned and resolved
|
||||||
Resolved,
|
Resolved {},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// User-generated platform moderation report.
|
/// User-generated platform moderation report.
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, OptionalStruct, Clone)]
|
||||||
|
#[optional_derive(Serialize, Deserialize, JsonSchema, Debug, Default, Clone)]
|
||||||
|
#[optional_name = "PartialReport"]
|
||||||
|
#[opt_skip_serializing_none]
|
||||||
pub struct Report {
|
pub struct Report {
|
||||||
/// Unique Id
|
/// Unique Id
|
||||||
#[serde(rename = "_id")]
|
#[serde(rename = "_id")]
|
||||||
@@ -98,6 +101,10 @@ pub struct Report {
|
|||||||
/// Additional report context
|
/// Additional report context
|
||||||
pub additional_context: String,
|
pub additional_context: String,
|
||||||
/// Status of the report
|
/// Status of the report
|
||||||
|
#[opt_passthrough]
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub status: ReportStatus,
|
pub status: ReportStatus,
|
||||||
|
/// Additional notes included on the report
|
||||||
|
#[serde(default)]
|
||||||
|
pub notes: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::models::report::PartialReport;
|
||||||
use crate::models::Report;
|
use crate::models::Report;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
@@ -6,6 +7,12 @@ pub trait AbstractReport: Sync + Send {
|
|||||||
/// Insert a new report into the database
|
/// Insert a new report into the database
|
||||||
async fn insert_report(&self, report: &Report) -> Result<()>;
|
async fn insert_report(&self, report: &Report) -> Result<()>;
|
||||||
|
|
||||||
|
/// Update a given report with new information
|
||||||
|
async fn update_report(&self, id: &str, message: &PartialReport) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch report
|
||||||
|
async fn fetch_report(&self, report_id: &str) -> Result<Report>;
|
||||||
|
|
||||||
/// Fetch reports
|
/// Fetch reports
|
||||||
async fn fetch_reports(&self) -> Result<Vec<Report>>;
|
async fn fetch_reports(&self) -> Result<Vec<Report>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::models::{Bot, Channel, Emoji, Invite, Member, Message, Server, ServerBan, User};
|
use crate::models::{
|
||||||
|
Bot, Channel, Emoji, Invite, Member, Message, Report, Server, ServerBan, User,
|
||||||
|
};
|
||||||
use crate::presence::presence_is_online;
|
use crate::presence::presence_is_online;
|
||||||
use crate::{Database, Error, Result};
|
use crate::{Database, Error, Result};
|
||||||
|
|
||||||
@@ -78,6 +80,11 @@ impl Ref {
|
|||||||
pub async fn as_emoji(&self, db: &Database) -> Result<Emoji> {
|
pub async fn as_emoji(&self, db: &Database) -> Result<Emoji> {
|
||||||
db.fetch_emoji(&self.id).await
|
db.fetch_emoji(&self.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch report from Ref
|
||||||
|
pub async fn as_report(&self, db: &Database) -> Result<Report> {
|
||||||
|
db.fetch_report(&self.id).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> FromParam<'r> for Ref {
|
impl<'r> FromParam<'r> for Ref {
|
||||||
|
|||||||
Reference in New Issue
Block a user