"pq-sys",
"r2d2",
"serde_json",
- "uuid 0.6.5",
]
[[package]]
"strum",
"strum_macros",
"url",
- "uuid 0.6.5",
]
[[package]]
"log",
"serde 1.0.117",
"serde_json",
- "uuid 0.6.5",
]
[[package]]
do_websocket_operation::<CreatePostLike>(context, id, op, data).await
}
UserOperation::SavePost => do_websocket_operation::<SavePost>(context, id, op, data).await,
- UserOperation::CreatePostReport => {
- do_websocket_operation::<CreatePostReport>(context, id, op, data).await
- }
- UserOperation::ListPostReports => {
- do_websocket_operation::<ListPostReports>(context, id, op, data).await
- }
- UserOperation::ResolvePostReport => {
- do_websocket_operation::<ResolvePostReport>(context, id, op, data).await
- }
// Comment ops
UserOperation::CreateComment => {
UserOperation::CreateCommentLike => {
do_websocket_operation::<CreateCommentLike>(context, id, op, data).await
}
- UserOperation::CreateCommentReport => {
- do_websocket_operation::<CreateCommentReport>(context, id, op, data).await
- },
- UserOperation::ListCommentReports => {
- do_websocket_operation::<ListCommentReports>(context, id, op, data).await
- },
- UserOperation::ResolveCommentReport => {
- do_websocket_operation::<ResolveCommentReport>(context, id, op, data).await
+
+ // report ops
+ UserOperation::CreateReport => {
+ do_websocket_operation::<CreateReport>(context, id, op, data).await
+ }
+ UserOperation::ListReports => {
+ do_websocket_operation::<ListReports>(context, id, op, data).await
+ }
+ UserOperation::ResolveReport => {
+ do_websocket_operation::<ResolveReport>(context, id, op, data).await
}
UserOperation::GetReportCount => {
do_websocket_operation::<GetReportCount>(context, id, op, data).await
use actix_web::web::Data;
+use std::str::FromStr;
-use lemmy_db::{
- comment_report::*,
- comment_view::*,
- community_view::*,
- post_report::*,
- post_view::*,
- Reportable,
- user_view::UserView,
-};
+use lemmy_db::{comment_report::*, comment_view::*, post_report::*, post_view::*, Reportable, ReportType,};
use lemmy_structs::{blocking, report::*};
use lemmy_utils::{APIError, ConnectionId, LemmyError};
-use lemmy_websocket::LemmyContext;
+use lemmy_websocket::{LemmyContext, UserOperation, messages::SendUserRoomMessage};
-use crate::{check_community_ban, get_user_from_jwt, Perform};
+use crate::{check_community_ban, get_user_from_jwt, is_mod_or_admin, Perform};
const MAX_REPORT_LEN: usize = 1000;
#[async_trait::async_trait(?Send)]
-impl Perform for CreateCommentReport {
- type Response = CommentReportResponse;
+impl Perform for CreateReport {
+ type Response = CreateReportResponse;
async fn perform(
&self,
context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<CommentReportResponse, LemmyError> {
- let data: &CreateCommentReport = &self;
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<CreateReportResponse, LemmyError> {
+ let data: &CreateReport = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?;
-
- // Check size of report and check for whitespace
- let reason: Option<String> = match data.reason.clone() {
- Some(s) if s.trim().is_empty() => None,
- Some(s) if s.len() > MAX_REPORT_LEN => {
- return Err(APIError::err("report_too_long").into());
- }
- Some(s) => Some(s),
- None => None,
- };
-
- // Fetch comment information
- let comment_id = data.comment;
- let comment = blocking(context.pool(), move |conn| CommentView::read(&conn, comment_id, None)).await??;
-
- // Check for community ban
- check_community_ban(user.id, comment.community_id, context.pool()).await?;
-
- // Insert the report
- let comment_time = match comment.updated {
- Some(s) => s,
- None => comment.published,
- };
- let report_form = CommentReportForm {
- time: None, // column defaults to now() in table
- reason,
- resolved: None, // column defaults to false
- user_id: user.id,
- comment_id,
- comment_text: comment.content,
- comment_time,
- };
- blocking(context.pool(), move |conn| CommentReport::report(conn, &report_form)).await??;
-
- Ok(CommentReportResponse { success: true })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for CreatePostReport {
- type Response = PostReportResponse;
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<PostReportResponse, LemmyError> {
- let data: &CreatePostReport = &self;
- let user = get_user_from_jwt(&data.auth, context.pool()).await?;
+ // check size of report and check for whitespace
+ let reason = data.reason.clone();
+ if reason.trim().is_empty() {
+ return Err(APIError::err("report_reason_required").into());
+ }
+ if reason.len() > MAX_REPORT_LEN {
+ return Err(APIError::err("report_too_long").into());
+ }
- // Check size of report and check for whitespace
- let reason: Option<String> = match data.reason.clone() {
- Some(s) if s.trim().is_empty() => None,
- Some(s) if s.len() > MAX_REPORT_LEN => {
- return Err(APIError::err("report_too_long").into());
- }
- Some(s) => Some(s),
- None => None,
- };
+ let report_type = ReportType::from_str(&data.report_type)?;
+ let user_id = user.id;
+ match report_type {
+ ReportType::Comment => { create_comment_report(context, data, user_id).await?; }
+ ReportType::Post => { create_post_report(context, data, user_id).await?; }
+ }
- // Fetch post information from the database
- let post_id = data.post;
- let post = blocking(context.pool(), move |conn| PostView::read(&conn, post_id, None)).await??;
+ // to build on this, the user should get a success response, however
+ // mods should get a different response with more details
+ let res = CreateReportResponse { success: true };
- // Check for community ban
- check_community_ban(user.id, post.community_id, context.pool()).await?;
+ context.chat_server().do_send(SendUserRoomMessage {
+ op: UserOperation::CreateReport,
+ response: res.clone(),
+ recipient_id: user.id,
+ websocket_id,
+ });
- // Insert the report
- let post_time = match post.updated {
- Some(s) => s,
- None => post.published,
- };
- let report_form = PostReportForm {
- time: None, // column defaults to now() in table
- reason,
- resolved: None, // column defaults to false
- user_id: user.id,
- post_id,
- post_name: post.name,
- post_url: post.url,
- post_body: post.body,
- post_time,
- };
- blocking(context.pool(), move |conn| PostReport::report(conn, &report_form)).await??;
-
- Ok(PostReportResponse { success: true })
+ Ok(res)
}
}
async fn perform(
&self,
context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
+ websocket_id: Option<ConnectionId>,
) -> Result<GetReportCountResponse, LemmyError> {
let data: &GetReportCount = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?;
-
let community_id = data.community;
- //Check community exists.
- let community_id = blocking(context.pool(), move |conn| {
- CommunityView::read(conn, community_id, None)
- })
- .await??
- .id;
-
- // Check community ban
- check_community_ban(user.id, data.community, context.pool()).await?;
-
- let mut mod_ids: Vec<i32> = Vec::new();
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, community_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
- if !mod_ids.contains(&user.id) {
- return Err(APIError::err("report_view_not_allowed").into());
- }
+
+ // Check for mod/admin privileges
+ is_mod_or_admin(context.pool(), user.id, community_id).await?;
let comment_reports = blocking(context.pool(), move |conn| {
CommentReportQueryBuilder::create(conn)
})
.await??;
- let response = GetReportCountResponse {
+ let res = GetReportCountResponse {
community: community_id,
comment_reports,
post_reports,
};
- Ok(response)
+ context.chat_server().do_send(SendUserRoomMessage {
+ op: UserOperation::ListReports,
+ response: res.clone(),
+ recipient_id: user.id,
+ websocket_id,
+ });
+
+ Ok(res)
}
}
#[async_trait::async_trait(?Send)]
-impl Perform for ListCommentReports {
- type Response = ListCommentReportResponse;
+impl Perform for ListReports {
+ type Response = ListReportsResponse;
async fn perform(
&self,
context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<ListCommentReportResponse, LemmyError> {
- let data: &ListCommentReports = &self;
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<ListReportsResponse, LemmyError> {
+ let data: &ListReports = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?;
-
let community_id = data.community;
- //Check community exists.
- let community_id = blocking(context.pool(), move |conn| {
- CommunityView::read(conn, community_id, None)
- })
- .await??
- .id;
-
- check_community_ban(user.id, data.community, context.pool()).await?;
-
- let mut mod_ids: Vec<i32> = Vec::new();
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, community_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
- if !mod_ids.contains(&user.id) {
- return Err(APIError::err("report_view_not_allowed").into());
- }
+
+ // Check for mod/admin privileges
+ is_mod_or_admin(context.pool(), user.id, community_id).await?;
let page = data.page;
let limit = data.limit;
- let reports = blocking(context.pool(), move |conn| {
+ let comments = blocking(context.pool(), move |conn| {
CommentReportQueryBuilder::create(conn)
.community_id(community_id)
.page(page)
})
.await??;
- Ok(ListCommentReportResponse { reports })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for ListPostReports {
- type Response = ListPostReportResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<ListPostReportResponse, LemmyError> {
- let data: &ListPostReports = &self;
- let user = get_user_from_jwt(&data.auth, context.pool()).await?;
-
- let community_id = data.community;
- //Check community exists.
- let community_id = blocking(context.pool(), move |conn| {
- CommunityView::read(conn, community_id, None)
- })
- .await??
- .id;
- // Check for community ban
- check_community_ban(user.id, data.community, context.pool()).await?;
-
- let mut mod_ids: Vec<i32> = Vec::new();
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, community_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
- if !mod_ids.contains(&user.id) {
- return Err(APIError::err("report_view_not_allowed").into());
- }
-
- let page = data.page;
- let limit = data.limit;
- let reports = blocking(context.pool(), move |conn| {
+ let posts = blocking(context.pool(), move |conn| {
PostReportQueryBuilder::create(conn)
- .community_id(community_id)
- .page(page)
- .limit(limit)
- .list()
+ .community_id(community_id)
+ .page(page)
+ .limit(limit)
+ .list()
})
- .await??;
+ .await??;
+
+ let res = ListReportsResponse { comments, posts };
- Ok(ListPostReportResponse { reports })
+ context.chat_server().do_send(SendUserRoomMessage {
+ op: UserOperation::ListReports,
+ response: res.clone(),
+ recipient_id: user.id,
+ websocket_id,
+ });
+
+ Ok(res)
}
}
#[async_trait::async_trait(?Send)]
-impl Perform for ResolveCommentReport {
- type Response = ResolveCommentReportResponse;
+impl Perform for ResolveReport {
+ type Response = ResolveReportResponse;
async fn perform(
&self,
context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<ResolveCommentReportResponse, LemmyError> {
- let data: &ResolveCommentReport = &self;
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<ResolveReportResponse, LemmyError> {
+ let data: &ResolveReport = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?;
- // Fetch the report view
- let report_id = data.report;
- let report = blocking(context.pool(), move |conn| CommentReportView::read(&conn, &report_id)).await??;
-
- // Check for community ban
- check_community_ban(user.id, report.community_id, context.pool()).await?;
-
- // Check for mod/admin privileges
- let mut mod_ids: Vec<i32> = Vec::new();
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, report.community_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
- if !mod_ids.contains(&user.id) {
- return Err(APIError::err("resolve_report_not_allowed").into());
+ let report_type = ReportType::from_str(&data.report_type)?;
+ let user_id = user.id;
+ match report_type {
+ ReportType::Comment => { resolve_comment_report(context, data, user_id).await?; }
+ ReportType::Post => { resolve_post_report(context, data, user_id).await?; }
}
- blocking(context.pool(), move |conn| {
- CommentReport::resolve(conn, &report_id.clone())
- })
- .await??;
-
- Ok(ResolveCommentReportResponse {
- report: report_id,
+ let report_id = data.report_id;
+ let res = ResolveReportResponse {
+ report_type: data.report_type.to_owned(),
+ report_id,
resolved: true,
- })
+ };
+
+ context.chat_server().do_send(SendUserRoomMessage {
+ op: UserOperation::ResolveReport,
+ response: res.clone(),
+ recipient_id: user.id,
+ websocket_id,
+ });
+
+ Ok(res)
}
}
-#[async_trait::async_trait(?Send)]
-impl Perform for ResolvePostReport {
- type Response = ResolvePostReportResponse;
+async fn create_comment_report(
+ context: &Data<LemmyContext>,
+ data: &CreateReport,
+ user_id: i32,
+) -> Result<(), LemmyError> {
+ let comment_id = data.entity_id;
+ let comment = blocking(context.pool(), move |conn| {
+ CommentView::read(&conn, comment_id, None)
+ }).await??;
+
+ check_community_ban(user_id, comment.community_id, context.pool()).await?;
+
+ let report_form = CommentReportForm {
+ creator_id: user_id,
+ comment_id,
+ comment_text: comment.content,
+ reason: data.reason.to_owned(),
+ };
+
+ return match blocking(context.pool(), move |conn| {
+ CommentReport::report(conn, &report_form)
+ }).await? {
+ Ok(_) => Ok(()),
+ Err(_e) => Err(APIError::err("couldnt_create_report").into())
+ };
+}
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<ResolvePostReportResponse, LemmyError> {
- let data: &ResolvePostReport = &self;
- let user = get_user_from_jwt(&data.auth, context.pool()).await?;
+async fn create_post_report(
+ context: &Data<LemmyContext>,
+ data: &CreateReport,
+ user_id: i32,
+) -> Result<(), LemmyError> {
+ let post_id = data.entity_id;
+ let post = blocking(context.pool(), move |conn| {
+ PostView::read(&conn, post_id, None)
+ }).await??;
+
+ check_community_ban(user_id, post.community_id, context.pool()).await?;
+
+ let report_form = PostReportForm {
+ creator_id: user_id,
+ post_id,
+ post_name: post.name,
+ post_url: post.url,
+ post_body: post.body,
+ reason: data.reason.to_owned(),
+ };
+
+ return match blocking(context.pool(), move |conn| {
+ PostReport::report(conn, &report_form)
+ }).await? {
+ Ok(_) => Ok(()),
+ Err(_e) => Err(APIError::err("couldnt_create_report").into())
+ };
+}
- // Fetch the report view
- let report_id = data.report;
- let report = blocking(context.pool(), move |conn| PostReportView::read(&conn, &report_id)).await??;
+async fn resolve_comment_report(
+ context: &Data<LemmyContext>,
+ data: &ResolveReport,
+ user_id: i32,
+) -> Result<(), LemmyError> {
+ let report_id = data.report_id;
+ let report = blocking(context.pool(), move |conn| {
+ CommentReportView::read(&conn, report_id)
+ }).await??;
+
+ is_mod_or_admin(context.pool(), user_id, report.community_id).await?;
+
+ let resolved = data.resolved;
+ let resolve_fun = move |conn: &'_ _| {
+ if resolved {
+ CommentReport::resolve(conn, report_id.clone(), user_id)
+ } else {
+ CommentReport::unresolve(conn, report_id.clone())
+ }
+ };
- // Check for community ban
- check_community_ban(user.id, report.community_id, context.pool()).await?;
+ if blocking(context.pool(),resolve_fun).await?.is_err() {
+ return Err(APIError::err("couldnt_resolve_report").into())
+ };
- // Check for mod/admin privileges
- let mut mod_ids: Vec<i32> = Vec::new();
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, report.community_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- mod_ids.append(
- &mut blocking(context.pool(), move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
- if !mod_ids.contains(&user.id) {
- return Err(APIError::err("resolve_report_not_allowed").into());
+ Ok(())
+}
+
+async fn resolve_post_report(
+ context: &Data<LemmyContext>,
+ data: &ResolveReport,
+ user_id: i32,
+) -> Result<(), LemmyError> {
+ let report_id = data.report_id;
+ let report = blocking(context.pool(), move |conn| {
+ PostReportView::read(&conn, report_id)
+ }).await??;
+
+ is_mod_or_admin(context.pool(), user_id, report.community_id).await?;
+
+ let resolved = data.resolved;
+ let resolve_fun = move |conn: &'_ _| {
+ if resolved {
+ PostReport::resolve(conn, report_id.clone(), user_id)
+ } else {
+ PostReport::unresolve(conn, report_id.clone())
}
+ };
- blocking(context.pool(), move |conn| {
- PostReport::resolve(conn, &report_id.clone())
- })
- .await??;
+ if blocking(context.pool(),resolve_fun).await?.is_err() {
+ return Err(APIError::err("couldnt_resolve_report").into())
+ };
- Ok(ResolvePostReportResponse {
- report: report_id,
- resolved: true,
- })
- }
+ Ok(())
}
[dependencies]
lemmy_utils = { path = "../lemmy_utils" }
-diesel = { version = "1.4", features = ["postgres","chrono","r2d2","64-column-tables","serde_json", "uuid"] }
+diesel = { version = "1.4", features = ["postgres","chrono","r2d2","64-column-tables","serde_json"] }
chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"]}
url = { version = "2.1", features = ["serde"] }
lazy_static = "1.3"
regex = "1.3"
-uuid = { version = "0.6.5", features = ["serde", "v4"] }
-use diesel::{PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods, insert_into, update};
-use diesel::pg::Pg;
-use diesel::result::*;
+use diesel::{dsl::*, pg::Pg, result::Error, *};
use serde::{Deserialize, Serialize};
-use crate::{
- limit_and_offset,
- MaybeOptional,
- schema::comment_report,
- comment::Comment,
- Reportable,
-};
+use crate::{limit_and_offset, MaybeOptional, schema::comment_report, comment::Comment, Reportable, naive_now};
table! {
comment_report_view (id) {
- id -> Uuid,
- time -> Timestamp,
- reason -> Nullable<Text>,
- resolved -> Bool,
- user_id -> Int4,
+ id -> Int4,
+ creator_id -> Int4,
comment_id -> Int4,
comment_text -> Text,
- comment_time -> Timestamp,
+ reason -> Text,
+ resolved -> Bool,
+ resolver_id -> Nullable<Int4>,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
post_id -> Int4,
community_id -> Int4,
- user_name -> Varchar,
- creator_id -> Int4,
creator_name -> Varchar,
+ comment_creator_id -> Int4,
+ comment_creator_name -> Varchar,
}
}
#[belongs_to(Comment)]
#[table_name = "comment_report"]
pub struct CommentReport {
- pub id: uuid::Uuid,
- pub time: chrono::NaiveDateTime,
- pub reason: Option<String>,
- pub resolved: bool,
- pub user_id: i32,
+ pub id: i32,
+ pub creator_id: i32,
pub comment_id: i32,
pub comment_text: String,
- pub comment_time: chrono::NaiveDateTime,
+ pub reason: String,
+ pub resolved: bool,
+ pub resolver_id: Option<i32>,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
}
#[derive(Insertable, AsChangeset, Clone)]
#[table_name = "comment_report"]
pub struct CommentReportForm {
- pub time: Option<chrono::NaiveDateTime>,
- pub reason: Option<String>,
- pub resolved: Option<bool>,
- pub user_id: i32,
+ pub creator_id: i32,
pub comment_id: i32,
pub comment_text: String,
- pub comment_time: chrono::NaiveDateTime,
+ pub reason: String,
}
impl Reportable<CommentReportForm> for CommentReport {
.get_result::<Self>(conn)
}
- fn resolve(conn: &PgConnection, report_id: &uuid::Uuid) -> Result<usize, Error> {
+ fn resolve(conn: &PgConnection, report_id: i32, by_user_id: i32) -> Result<usize, Error> {
use crate::schema::comment_report::dsl::*;
update(comment_report.find(report_id))
- .set(resolved.eq(true))
+ .set((
+ resolved.eq(true),
+ resolver_id.eq(by_user_id),
+ updated.eq(naive_now()),
+ ))
+ .execute(conn)
+ }
+
+ fn unresolve(conn: &PgConnection, report_id: i32) -> Result<usize, Error> {
+ use crate::schema::comment_report::dsl::*;
+ update(comment_report.find(report_id))
+ .set((
+ resolved.eq(false),
+ updated.eq(naive_now()),
+ ))
.execute(conn)
}
}
#[derive(
- Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
+ Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
)]
#[table_name = "comment_report_view"]
pub struct CommentReportView {
- pub id: uuid::Uuid,
- pub time: chrono::NaiveDateTime,
- pub reason: Option<String>,
- pub resolved: bool,
- pub user_id: i32,
+ pub id: i32,
+ pub creator_id: i32,
pub comment_id: i32,
pub comment_text: String,
- pub comment_time: chrono::NaiveDateTime,
+ pub reason: String,
+ pub resolved: bool,
+ pub resolver_id: Option<i32>,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
pub post_id: i32,
pub community_id: i32,
- pub user_name: String,
- pub creator_id: i32,
pub creator_name: String,
+ pub comment_creator_id: i32,
+ pub comment_creator_name: String,
}
pub struct CommentReportQueryBuilder<'a> {
}
impl CommentReportView {
- pub fn read(conn: &PgConnection, report_id: &uuid::Uuid) -> Result<Self, Error> {
+ pub fn read(conn: &PgConnection, report_id: i32) -> Result<Self, Error> {
use super::comment_report::comment_report_view::dsl::*;
comment_report_view
.filter(id.eq(report_id))
let (limit, offset) = limit_and_offset(self.page, self.limit);
query
- .order_by(time.desc())
+ .order_by(published.desc())
.limit(limit)
.offset(offset)
.load::<CommentReportView>(self.conn)
fn report(conn: &PgConnection, form: &T) -> Result<Self, Error>
where
Self: Sized;
- fn resolve(conn: &PgConnection, report_id: &uuid::Uuid) -> Result<usize, Error>
+ fn resolve(conn: &PgConnection, report_id: i32, user_id: i32) -> Result<usize, Error>
+ where
+ Self: Sized;
+ fn unresolve(conn: &PgConnection, report_id: i32) -> Result<usize, Error>
where
Self: Sized;
}
Url,
}
+#[derive(EnumString, ToString, Debug, Serialize, Deserialize)]
+pub enum ReportType {
+ Comment,
+ Post,
+}
+
pub fn fuzzy_search(q: &str) -> String {
let replaced = q.replace(" ", "%");
format!("%{}%", replaced)
-use diesel::{PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods, insert_into, update};
-use diesel::pg::Pg;
-use diesel::result::*;
+use diesel::{dsl::*, pg::Pg, result::Error, *};
use serde::{Deserialize, Serialize};
-use crate::{
- limit_and_offset,
- MaybeOptional,
- schema::post_report,
- post::Post,
- Reportable,
-};
+use crate::{limit_and_offset, MaybeOptional, schema::post_report, post::Post, Reportable, naive_now};
table! {
post_report_view (id) {
- id -> Uuid,
- time -> Timestamp,
- reason -> Nullable<Text>,
- resolved -> Bool,
- user_id -> Int4,
- post_id -> Int4,
- post_name -> Varchar,
- post_url -> Nullable<Text>,
- post_body -> Nullable<Text>,
- post_time -> Timestamp,
- community_id -> Int4,
- user_name -> Varchar,
- creator_id -> Int4,
- creator_name -> Varchar,
+ id -> Int4,
+ creator_id -> Int4,
+ post_id -> Int4,
+ post_name -> Varchar,
+ post_url -> Nullable<Text>,
+ post_body -> Nullable<Text>,
+ reason -> Text,
+ resolved -> Bool,
+ resolver_id -> Nullable<Int4>,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
+ community_id -> Int4,
+ creator_name -> Varchar,
+ post_creator_id -> Int4,
+ post_creator_name -> Varchar,
}
}
#[belongs_to(Post)]
#[table_name = "post_report"]
pub struct PostReport {
- pub id: uuid::Uuid,
- pub time: chrono::NaiveDateTime,
- pub reason: Option<String>,
- pub resolved: bool,
- pub user_id: i32,
+ pub id: i32,
+ pub creator_id: i32,
pub post_id: i32,
pub post_name: String,
pub post_url: Option<String>,
pub post_body: Option<String>,
- pub post_time: chrono::NaiveDateTime,
+ pub reason: String,
+ pub resolved: bool,
+ pub resolver_id: Option<i32>,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
}
#[derive(Insertable, AsChangeset, Clone)]
#[table_name = "post_report"]
pub struct PostReportForm {
- pub time: Option<chrono::NaiveDateTime>,
- pub reason: Option<String>,
- pub resolved: Option<bool>,
- pub user_id: i32,
+ pub creator_id: i32,
pub post_id: i32,
pub post_name: String,
pub post_url: Option<String>,
pub post_body: Option<String>,
- pub post_time: chrono::NaiveDateTime,
+ pub reason: String,
}
impl Reportable<PostReportForm> for PostReport {
.get_result::<Self>(conn)
}
- fn resolve(conn: &PgConnection, report_id: &uuid::Uuid) -> Result<usize, Error> {
+ fn resolve(conn: &PgConnection, report_id: i32, by_user_id: i32) -> Result<usize, Error> {
use crate::schema::post_report::dsl::*;
update(post_report.find(report_id))
- .set(resolved.eq(true))
+ .set((
+ resolved.eq(true),
+ resolver_id.eq(by_user_id),
+ updated.eq(naive_now()),
+ ))
+ .execute(conn)
+ }
+
+ fn unresolve(conn: &PgConnection, report_id: i32) -> Result<usize, Error> {
+ use crate::schema::post_report::dsl::*;
+ update(post_report.find(report_id))
+ .set((
+ resolved.eq(false),
+ updated.eq(naive_now()),
+ ))
.execute(conn)
}
}
#[derive(
-Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
+Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
)]
#[table_name = "post_report_view"]
pub struct PostReportView {
- pub id: uuid::Uuid,
- pub time: chrono::NaiveDateTime,
- pub reason: Option<String>,
- pub resolved: bool,
- pub user_id: i32,
+ pub id: i32,
+ pub creator_id: i32,
pub post_id: i32,
pub post_name: String,
pub post_url: Option<String>,
pub post_body: Option<String>,
- pub post_time: chrono::NaiveDateTime,
+ pub reason: String,
+ pub resolved: bool,
+ pub resolver_id: Option<i32>,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
pub community_id: i32,
- pub user_name: String,
- pub creator_id: i32,
pub creator_name: String,
+ pub post_creator_id: i32,
+ pub post_creator_name: String,
}
impl PostReportView {
- pub fn read(conn: &PgConnection, report_id: &uuid::Uuid) -> Result<Self, Error> {
+ pub fn read(conn: &PgConnection, report_id: i32) -> Result<Self, Error> {
use super::post_report::post_report_view::dsl::*;
post_report_view
.filter(id.eq(report_id))
let (limit, offset) = limit_and_offset(self.page, self.limit);
query
- .order_by(time.desc())
+ .order_by(published.desc())
.limit(limit)
.offset(offset)
.load::<PostReportView>(self.conn)
table! {
comment_report (id) {
- id -> Uuid,
- time -> Timestamp,
- reason -> Nullable<Text>,
- resolved -> Bool,
- user_id -> Int4,
+ id -> Int4,
+ creator_id -> Int4,
comment_id -> Int4,
comment_text -> Text,
- comment_time -> Timestamp,
+ reason -> Text,
+ resolved -> Bool,
+ resolver_id -> Nullable<Int4>,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
}
}
table! {
post_report (id) {
- id -> Uuid,
- time -> Timestamp,
- reason -> Nullable<Text>,
- resolved -> Bool,
- user_id -> Int4,
+ id -> Int4,
+ creator_id -> Int4,
post_id -> Int4,
post_name -> Varchar,
post_url -> Nullable<Text>,
post_body -> Nullable<Text>,
- post_time -> Timestamp,
+ reason -> Text,
+ resolved -> Bool,
+ resolver_id -> Nullable<Int4>,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
}
}
joinable!(comment_like -> post (post_id));
joinable!(comment_like -> user_ (user_id));
joinable!(comment_report -> comment (comment_id));
-joinable!(comment_report -> user_ (user_id));
joinable!(comment_saved -> comment (comment_id));
joinable!(comment_saved -> user_ (user_id));
joinable!(community -> category (category_id));
joinable!(post_read -> post (post_id));
joinable!(post_read -> user_ (user_id));
joinable!(post_report -> post (post_id));
-joinable!(post_report -> user_ (user_id));
joinable!(post_saved -> post (post_id));
joinable!(post_saved -> user_ (user_id));
joinable!(site -> user_ (creator_id));
joinable!(user_mention -> user_ (recipient_id));
allow_tables_to_appear_in_same_query!(
- activity,
- category,
- comment,
- comment_aggregates_fast,
- comment_like,
- comment_report,
- comment_saved,
- community,
- community_aggregates_fast,
- community_follower,
- community_moderator,
- community_user_ban,
- mod_add,
- mod_add_community,
- mod_ban,
- mod_ban_from_community,
- mod_lock_post,
- mod_remove_comment,
- mod_remove_community,
- mod_remove_post,
- mod_sticky_post,
- password_reset_request,
- post,
- post_aggregates_fast,
- post_like,
- post_read,
- post_report,
- post_saved,
- private_message,
- site,
- user_,
- user_ban,
- user_fast,
- user_mention,
+ activity,
+ category,
+ comment,
+ comment_aggregates_fast,
+ comment_like,
+ comment_report,
+ comment_saved,
+ community,
+ community_aggregates_fast,
+ community_follower,
+ community_moderator,
+ community_user_ban,
+ mod_add,
+ mod_add_community,
+ mod_ban,
+ mod_ban_from_community,
+ mod_lock_post,
+ mod_remove_comment,
+ mod_remove_community,
+ mod_remove_post,
+ mod_sticky_post,
+ password_reset_request,
+ post,
+ post_aggregates_fast,
+ post_like,
+ post_read,
+ post_report,
+ post_saved,
+ private_message,
+ site,
+ user_,
+ user_ban,
+ user_fast,
+ user_mention,
);
diesel = "1.4"
actix-web = { version = "3.0" }
chrono = { version = "0.4", features = ["serde"] }
-serde_json = { version = "1.0", features = ["preserve_order"]}
-uuid = { version = "0.6.5", features = ["serde", "v4"] }
\ No newline at end of file
+serde_json = { version = "1.0", features = ["preserve_order"]}
\ No newline at end of file
-use lemmy_db::{
- comment_report::CommentReportView,
- post_report::PostReportView,
-};
+use lemmy_db::{comment_report::CommentReportView, post_report::PostReportView};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
-pub struct CreateCommentReport {
- pub comment: i32,
- pub reason: Option<String>,
+pub struct CreateReport {
+ pub report_type: String,
+ pub entity_id: i32,
+ pub reason: String,
pub auth: String,
}
#[derive(Serialize, Deserialize, Clone)]
-pub struct CommentReportResponse {
+pub struct CreateReportResponse {
pub success: bool,
}
-#[derive(Serialize, Deserialize)]
-pub struct CreatePostReport {
- pub post: i32,
- pub reason: Option<String>,
- pub auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct PostReportResponse {
- pub success: bool,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct ListCommentReports {
- pub page: Option<i64>,
- pub limit: Option<i64>,
- pub community: i32,
- pub auth: String,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct ListCommentReportResponse {
- pub reports: Vec<CommentReportView>,
-}
-
#[derive(Serialize, Deserialize, Debug)]
-pub struct ListPostReports {
+pub struct ListReports {
pub page: Option<i64>,
pub limit: Option<i64>,
pub community: i32,
pub auth: String,
}
-#[derive(Serialize, Deserialize, Debug)]
-pub struct ListPostReportResponse {
- pub reports: Vec<PostReportView>,
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct ListReportsResponse {
+ pub posts: Vec<PostReportView>,
+ pub comments: Vec<CommentReportView>,
}
#[derive(Serialize, Deserialize, Debug)]
pub auth: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct GetReportCountResponse {
pub community: i32,
pub comment_reports: usize,
}
#[derive(Serialize, Deserialize, Debug)]
-pub struct ResolveCommentReport {
- pub report: uuid::Uuid,
- pub auth: String,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct ResolveCommentReportResponse {
- pub report: uuid::Uuid,
+pub struct ResolveReport {
+ pub report_type: String,
+ pub report_id: i32,
pub resolved: bool,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct ResolvePostReport {
- pub report: uuid::Uuid,
pub auth: String,
}
-#[derive(Serialize, Deserialize, Debug)]
-pub struct ResolvePostReportResponse {
- pub report: uuid::Uuid,
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct ResolveReportResponse {
+ pub report_type: String,
+ pub report_id: i32,
pub resolved: bool,
}
MarkCommentAsRead,
SaveComment,
CreateCommentLike,
- CreateCommentReport,
- ListCommentReports,
- ResolveCommentReport,
GetPosts,
CreatePostLike,
EditPost,
LockPost,
StickyPost,
SavePost,
- CreatePostReport,
- ListPostReports,
- ResolvePostReport,
+ CreateReport,
+ ResolveReport,
+ ListReports,
+ GetReportCount,
EditCommunity,
DeleteCommunity,
RemoveCommunity,
GetUserMentions,
MarkUserMentionAsRead,
GetModlog,
- GetReportCount,
BanFromCommunity,
AddModToCommunity,
CreateSite,
drop view post_report_view;
drop table comment_report;
drop table post_report;
-drop extension "uuid-ossp";
-create extension "uuid-ossp";
-
create table comment_report (
- id uuid primary key default uuid_generate_v4(),
- time timestamp not null default now(),
- reason text,
- resolved bool not null default false,
- user_id int references user_ on update cascade on delete cascade not null, -- user reporting comment
+ id serial primary key,
+ creator_id int references user_ on update cascade on delete cascade not null, -- user reporting comment
comment_id int references comment on update cascade on delete cascade not null, -- comment being reported
comment_text text not null,
- comment_time timestamp not null,
- unique(comment_id, user_id) -- users should only be able to report a comment once
+ reason text not null,
+ resolved bool not null default false,
+ resolver_id int references user_ on update cascade on delete cascade not null, -- user resolving report
+ published timestamp not null default now(),
+ updated timestamp null,
+ unique(comment_id, creator_id) -- users should only be able to report a comment once
);
create table post_report (
- id uuid primary key default uuid_generate_v4(),
- time timestamp not null default now(),
- reason text,
- resolved bool not null default false,
- user_id int references user_ on update cascade on delete cascade not null, -- user reporting post
+ id serial primary key,
+ creator_id int references user_ on update cascade on delete cascade not null, -- user reporting post
post_id int references post on update cascade on delete cascade not null, -- post being reported
- post_name varchar(100) not null,
+ post_name varchar(100) not null,
post_url text,
post_body text,
- post_time timestamp not null,
- unique(post_id, user_id) -- users should only be able to report a post once
+ reason text not null,
+ resolved bool not null default false,
+ resolver_id int references user_ on update cascade on delete cascade not null, -- user resolving report
+ published timestamp not null default now(),
+ updated timestamp null,
+ unique(post_id, creator_id) -- users should only be able to report a post once
);
create or replace view comment_report_view as
select cr.*,
c.post_id,
p.community_id,
-f.name as user_name,
-u.id as creator_id,
-u.name as creator_name
+f.name as creator_name,
+u.id as comment_creator_id,
+u.name as comment_creator_name
from comment_report cr
left join comment c on c.id = cr.comment_id
left join post p on p.id = c.post_id
left join user_ u on u.id = c.creator_id
-left join user_ f on f.id = cr.user_id;
+left join user_ f on f.id = cr.creator_id;
create or replace view post_report_view as
select pr.*,
p.community_id,
-f.name as user_name,
-u.id as creator_id,
-u.name as creator_name
+f.name as creator_name,
+u.id as post_creator_id,
+u.name as post_creator_name
from post_report pr
left join post p on p.id = pr.post_id
left join user_ u on u.id = p.creator_id
-left join user_ f on f.id = pr.user_id;
+left join user_ f on f.id = pr.creator_id;
.route("/ban_user", web::post().to(route_post::<BanFromCommunity>))
.route("/mod", web::post().to(route_post::<AddModToCommunity>))
.route("/join", web::post().to(route_post::<CommunityJoin>))
- .route("/comment_reports",web::get().to(route_get::<ListCommentReports>))
- .route("/post_reports", web::get().to(route_get::<ListPostReports>))
- .route("/reports", web::get().to(route_get::<GetReportCount>)),
)
// Post
.service(
.route("/like", web::post().to(route_post::<CreatePostLike>))
.route("/save", web::put().to(route_post::<SavePost>))
.route("/join", web::post().to(route_post::<PostJoin>))
- .route("/report", web::put().to(route_post::<CreatePostReport>))
- .route("/resolve_report",web::post().to(route_post::<ResolvePostReport>)),
)
// Comment
.service(
- web::scope("/comment")
+ web::scope("/comment")
.wrap(rate_limit.message())
.route("", web::post().to(route_post::<CreateComment>))
.route("", web::put().to(route_post::<EditComment>))
.route("/like", web::post().to(route_post::<CreateCommentLike>))
.route("/save", web::put().to(route_post::<SaveComment>))
.route("/list", web::get().to(route_get::<GetComments>))
- .route("/report", web::put().to(route_post::<CreateCommentReport>))
- .route("/resolve_report",web::post().to(route_post::<ResolveCommentReport>)),
)
// Private Message
.service(
web::resource("/admin/add")
.wrap(rate_limit.message())
.route(web::post().to(route_post::<AddAdmin>)),
+ )
+ // Reports
+ .service(
+ web::scope("/report")
+ .wrap(rate_limit.message())
+ .route("", web::get().to(route_get::<GetReportCount>))
+ .route("",web::post().to(route_post::<CreateReport>))
+ .route("/resolve",web::put().to(route_post::<ResolveReport>))
+ .route("/list", web::get().to(route_get::<ListReports>))
),
);
}