reports: update db tables, combine api impl

pull/1197/head
eiknat 2020-10-20 20:31:01 -04:00 committed by eiknat
parent 6d43202efb
commit d6b1c8df2f
14 changed files with 431 additions and 553 deletions

3
Cargo.lock generated
View File

@ -1054,7 +1054,6 @@ dependencies = [
"pq-sys", "pq-sys",
"r2d2", "r2d2",
"serde_json", "serde_json",
"uuid 0.6.5",
] ]
[[package]] [[package]]
@ -1834,7 +1833,6 @@ dependencies = [
"strum", "strum",
"strum_macros", "strum_macros",
"url", "url",
"uuid 0.6.5",
] ]
[[package]] [[package]]
@ -1900,7 +1898,6 @@ dependencies = [
"log", "log",
"serde 1.0.117", "serde 1.0.117",
"serde_json", "serde_json",
"uuid 0.6.5",
] ]
[[package]] [[package]]

View File

@ -267,15 +267,6 @@ pub async fn match_websocket_operation(
do_websocket_operation::<CreatePostLike>(context, id, op, data).await do_websocket_operation::<CreatePostLike>(context, id, op, data).await
} }
UserOperation::SavePost => do_websocket_operation::<SavePost>(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 // Comment ops
UserOperation::CreateComment => { UserOperation::CreateComment => {
@ -302,14 +293,16 @@ pub async fn match_websocket_operation(
UserOperation::CreateCommentLike => { UserOperation::CreateCommentLike => {
do_websocket_operation::<CreateCommentLike>(context, id, op, data).await do_websocket_operation::<CreateCommentLike>(context, id, op, data).await
} }
UserOperation::CreateCommentReport => {
do_websocket_operation::<CreateCommentReport>(context, id, op, data).await // report ops
}, UserOperation::CreateReport => {
UserOperation::ListCommentReports => { do_websocket_operation::<CreateReport>(context, id, op, data).await
do_websocket_operation::<ListCommentReports>(context, id, op, data).await }
}, UserOperation::ListReports => {
UserOperation::ResolveCommentReport => { do_websocket_operation::<ListReports>(context, id, op, data).await
do_websocket_operation::<ResolveCommentReport>(context, id, op, data).await }
UserOperation::ResolveReport => {
do_websocket_operation::<ResolveReport>(context, id, op, data).await
} }
UserOperation::GetReportCount => { UserOperation::GetReportCount => {
do_websocket_operation::<GetReportCount>(context, id, op, data).await do_websocket_operation::<GetReportCount>(context, id, op, data).await

View File

@ -1,119 +1,55 @@
use actix_web::web::Data; use actix_web::web::Data;
use std::str::FromStr;
use lemmy_db::{ use lemmy_db::{comment_report::*, comment_view::*, post_report::*, post_view::*, Reportable, ReportType,};
comment_report::*,
comment_view::*,
community_view::*,
post_report::*,
post_view::*,
Reportable,
user_view::UserView,
};
use lemmy_structs::{blocking, report::*}; use lemmy_structs::{blocking, report::*};
use lemmy_utils::{APIError, ConnectionId, LemmyError}; 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; const MAX_REPORT_LEN: usize = 1000;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for CreateCommentReport { impl Perform for CreateReport {
type Response = CommentReportResponse; type Response = CreateReportResponse;
async fn perform( async fn perform(
&self, &self,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommentReportResponse, LemmyError> { ) -> Result<CreateReportResponse, LemmyError> {
let data: &CreateCommentReport = &self; let data: &CreateReport = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_user_from_jwt(&data.auth, context.pool()).await?;
// Check size of report and check for whitespace // check size of report and check for whitespace
let reason: Option<String> = match data.reason.clone() { let reason = data.reason.clone();
Some(s) if s.trim().is_empty() => None, if reason.trim().is_empty() {
Some(s) if s.len() > MAX_REPORT_LEN => { return Err(APIError::err("report_reason_required").into());
return Err(APIError::err("report_too_long").into()); }
} if reason.len() > MAX_REPORT_LEN {
Some(s) => Some(s), return Err(APIError::err("report_too_long").into());
None => None, }
};
// Fetch comment information let report_type = ReportType::from_str(&data.report_type)?;
let comment_id = data.comment; let user_id = user.id;
let comment = blocking(context.pool(), move |conn| CommentView::read(&conn, comment_id, None)).await??; match report_type {
ReportType::Comment => { create_comment_report(context, data, user_id).await?; }
ReportType::Post => { create_post_report(context, data, user_id).await?; }
}
// Check for community ban // to build on this, the user should get a success response, however
check_community_ban(user.id, comment.community_id, context.pool()).await?; // mods should get a different response with more details
let res = CreateReportResponse { success: true };
// Insert the report context.chat_server().do_send(SendUserRoomMessage {
let comment_time = match comment.updated { op: UserOperation::CreateReport,
Some(s) => s, response: res.clone(),
None => comment.published, recipient_id: user.id,
}; websocket_id,
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 }) Ok(res)
}
}
#[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: 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 post information from the database
let post_id = data.post;
let post = blocking(context.pool(), move |conn| PostView::read(&conn, post_id, None)).await??;
// Check for community ban
check_community_ban(user.id, post.community_id, context.pool()).await?;
// 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 })
} }
} }
@ -124,39 +60,14 @@ impl Perform for GetReportCount {
async fn perform( async fn perform(
&self, &self,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<GetReportCountResponse, LemmyError> { ) -> Result<GetReportCountResponse, LemmyError> {
let data: &GetReportCount = &self; let data: &GetReportCount = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_user_from_jwt(&data.auth, context.pool()).await?;
let community_id = data.community; 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 for mod/admin privileges
check_community_ban(user.id, data.community, context.pool()).await?; is_mod_or_admin(context.pool(), user.id, community_id).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 comment_reports = blocking(context.pool(), move |conn| { let comment_reports = blocking(context.pool(), move |conn| {
CommentReportQueryBuilder::create(conn) CommentReportQueryBuilder::create(conn)
@ -173,59 +84,42 @@ impl Perform for GetReportCount {
}) })
.await??; .await??;
let response = GetReportCountResponse { let res = GetReportCountResponse {
community: community_id, community: community_id,
comment_reports, comment_reports,
post_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)] #[async_trait::async_trait(?Send)]
impl Perform for ListCommentReports { impl Perform for ListReports {
type Response = ListCommentReportResponse; type Response = ListReportsResponse;
async fn perform( async fn perform(
&self, &self,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<ListCommentReportResponse, LemmyError> { ) -> Result<ListReportsResponse, LemmyError> {
let data: &ListCommentReports = &self; let data: &ListReports = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_user_from_jwt(&data.auth, context.pool()).await?;
let community_id = data.community; 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?; // Check for mod/admin privileges
is_mod_or_admin(context.pool(), user.id, community_id).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 page = data.page;
let limit = data.limit; let limit = data.limit;
let reports = blocking(context.pool(), move |conn| { let comments = blocking(context.pool(), move |conn| {
CommentReportQueryBuilder::create(conn) CommentReportQueryBuilder::create(conn)
.community_id(community_id) .community_id(community_id)
.page(page) .page(page)
@ -234,161 +128,173 @@ impl Perform for ListCommentReports {
}) })
.await??; .await??;
Ok(ListCommentReportResponse { reports }) let posts = blocking(context.pool(), move |conn| {
}
}
#[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| {
PostReportQueryBuilder::create(conn) PostReportQueryBuilder::create(conn)
.community_id(community_id) .community_id(community_id)
.page(page) .page(page)
.limit(limit) .limit(limit)
.list() .list()
}) })
.await??; .await??;
Ok(ListPostReportResponse { reports }) let res = ListReportsResponse { comments, posts };
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)] #[async_trait::async_trait(?Send)]
impl Perform for ResolveCommentReport { impl Perform for ResolveReport {
type Response = ResolveCommentReportResponse; type Response = ResolveReportResponse;
async fn perform( async fn perform(
&self, &self,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<ResolveCommentReportResponse, LemmyError> { ) -> Result<ResolveReportResponse, LemmyError> {
let data: &ResolveCommentReport = &self; let data: &ResolveReport = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_user_from_jwt(&data.auth, context.pool()).await?;
// Fetch the report view let report_type = ReportType::from_str(&data.report_type)?;
let report_id = data.report; let user_id = user.id;
let report = blocking(context.pool(), move |conn| CommentReportView::read(&conn, &report_id)).await??; match report_type {
ReportType::Comment => { resolve_comment_report(context, data, user_id).await?; }
// Check for community ban ReportType::Post => { resolve_post_report(context, data, user_id).await?; }
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());
} }
blocking(context.pool(), move |conn| { let report_id = data.report_id;
CommentReport::resolve(conn, &report_id.clone()) let res = ResolveReportResponse {
}) report_type: data.report_type.to_owned(),
.await??; report_id,
Ok(ResolveCommentReportResponse {
report: report_id,
resolved: true, 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)] async fn create_comment_report(
impl Perform for ResolvePostReport { context: &Data<LemmyContext>,
type Response = ResolvePostReportResponse; 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??;
async fn perform( check_community_ban(user_id, comment.community_id, context.pool()).await?;
&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?;
// Fetch the report view let report_form = CommentReportForm {
let report_id = data.report; creator_id: user_id,
let report = blocking(context.pool(), move |conn| PostReportView::read(&conn, &report_id)).await??; comment_id,
comment_text: comment.content,
reason: data.reason.to_owned(),
};
// Check for community ban return match blocking(context.pool(), move |conn| {
check_community_ban(user.id, report.community_id, context.pool()).await?; CommentReport::report(conn, &report_form)
}).await? {
// Check for mod/admin privileges Ok(_) => Ok(()),
let mut mod_ids: Vec<i32> = Vec::new(); Err(_e) => Err(APIError::err("couldnt_create_report").into())
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()) async fn create_post_report(
}) context: &Data<LemmyContext>,
.await??, data: &CreateReport,
); user_id: i32,
mod_ids.append( ) -> Result<(), LemmyError> {
&mut blocking(context.pool(), move |conn| { let post_id = data.entity_id;
UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect()) let post = blocking(context.pool(), move |conn| {
}) PostView::read(&conn, post_id, None)
.await??, }).await??;
);
if !mod_ids.contains(&user.id) { check_community_ban(user_id, post.community_id, context.pool()).await?;
return Err(APIError::err("resolve_report_not_allowed").into());
} let report_form = PostReportForm {
creator_id: user_id,
blocking(context.pool(), move |conn| { post_id,
PostReport::resolve(conn, &report_id.clone()) post_name: post.name,
}) post_url: post.url,
.await??; post_body: post.body,
reason: data.reason.to_owned(),
Ok(ResolvePostReportResponse { };
report: report_id,
resolved: true, return match blocking(context.pool(), move |conn| {
}) PostReport::report(conn, &report_form)
} }).await? {
Ok(_) => Ok(()),
Err(_e) => Err(APIError::err("couldnt_create_report").into())
};
}
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())
}
};
if blocking(context.pool(),resolve_fun).await?.is_err() {
return Err(APIError::err("couldnt_resolve_report").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())
}
};
if blocking(context.pool(),resolve_fun).await?.is_err() {
return Err(APIError::err("couldnt_resolve_report").into())
};
Ok(())
} }

View File

@ -9,7 +9,7 @@ path = "src/lib.rs"
[dependencies] [dependencies]
lemmy_utils = { path = "../lemmy_utils" } 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"] } chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"]} serde_json = { version = "1.0", features = ["preserve_order"]}
@ -21,4 +21,3 @@ bcrypt = "0.8"
url = { version = "2.1", features = ["serde"] } url = { version = "2.1", features = ["serde"] }
lazy_static = "1.3" lazy_static = "1.3"
regex = "1.3" regex = "1.3"
uuid = { version = "0.6.5", features = ["serde", "v4"] }

View File

@ -1,31 +1,24 @@
use diesel::{PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods, insert_into, update}; use diesel::{dsl::*, pg::Pg, result::Error, *};
use diesel::pg::Pg;
use diesel::result::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{limit_and_offset, MaybeOptional, schema::comment_report, comment::Comment, Reportable, naive_now};
limit_and_offset,
MaybeOptional,
schema::comment_report,
comment::Comment,
Reportable,
};
table! { table! {
comment_report_view (id) { comment_report_view (id) {
id -> Uuid, id -> Int4,
time -> Timestamp, creator_id -> Int4,
reason -> Nullable<Text>,
resolved -> Bool,
user_id -> Int4,
comment_id -> Int4, comment_id -> Int4,
comment_text -> Text, comment_text -> Text,
comment_time -> Timestamp, reason -> Text,
resolved -> Bool,
resolver_id -> Nullable<Int4>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
post_id -> Int4, post_id -> Int4,
community_id -> Int4, community_id -> Int4,
user_name -> Varchar,
creator_id -> Int4,
creator_name -> Varchar, creator_name -> Varchar,
comment_creator_id -> Int4,
comment_creator_name -> Varchar,
} }
} }
@ -33,26 +26,24 @@ table! {
#[belongs_to(Comment)] #[belongs_to(Comment)]
#[table_name = "comment_report"] #[table_name = "comment_report"]
pub struct CommentReport { pub struct CommentReport {
pub id: uuid::Uuid, pub id: i32,
pub time: chrono::NaiveDateTime, pub creator_id: i32,
pub reason: Option<String>,
pub resolved: bool,
pub user_id: i32,
pub comment_id: i32, pub comment_id: i32,
pub comment_text: String, 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)] #[derive(Insertable, AsChangeset, Clone)]
#[table_name = "comment_report"] #[table_name = "comment_report"]
pub struct CommentReportForm { pub struct CommentReportForm {
pub time: Option<chrono::NaiveDateTime>, pub creator_id: i32,
pub reason: Option<String>,
pub resolved: Option<bool>,
pub user_id: i32,
pub comment_id: i32, pub comment_id: i32,
pub comment_text: String, pub comment_text: String,
pub comment_time: chrono::NaiveDateTime, pub reason: String,
} }
impl Reportable<CommentReportForm> for CommentReport { impl Reportable<CommentReportForm> for CommentReport {
@ -63,32 +54,47 @@ impl Reportable<CommentReportForm> for CommentReport {
.get_result::<Self>(conn) .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::*; use crate::schema::comment_report::dsl::*;
update(comment_report.find(report_id)) 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) .execute(conn)
} }
} }
#[derive( #[derive(
Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
)] )]
#[table_name = "comment_report_view"] #[table_name = "comment_report_view"]
pub struct CommentReportView { pub struct CommentReportView {
pub id: uuid::Uuid, pub id: i32,
pub time: chrono::NaiveDateTime, pub creator_id: i32,
pub reason: Option<String>,
pub resolved: bool,
pub user_id: i32,
pub comment_id: i32, pub comment_id: i32,
pub comment_text: String, 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 post_id: i32,
pub community_id: i32, pub community_id: i32,
pub user_name: String,
pub creator_id: i32,
pub creator_name: String, pub creator_name: String,
pub comment_creator_id: i32,
pub comment_creator_name: String,
} }
pub struct CommentReportQueryBuilder<'a> { pub struct CommentReportQueryBuilder<'a> {
@ -101,7 +107,7 @@ pub struct CommentReportQueryBuilder<'a> {
} }
impl CommentReportView { 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::*; use super::comment_report::comment_report_view::dsl::*;
comment_report_view comment_report_view
.filter(id.eq(report_id)) .filter(id.eq(report_id))
@ -161,7 +167,7 @@ impl<'a> CommentReportQueryBuilder<'a> {
let (limit, offset) = limit_and_offset(self.page, self.limit); let (limit, offset) = limit_and_offset(self.page, self.limit);
query query
.order_by(time.desc()) .order_by(published.desc())
.limit(limit) .limit(limit)
.offset(offset) .offset(offset)
.load::<CommentReportView>(self.conn) .load::<CommentReportView>(self.conn)

View File

@ -115,7 +115,10 @@ pub trait Reportable<T> {
fn report(conn: &PgConnection, form: &T) -> Result<Self, Error> fn report(conn: &PgConnection, form: &T) -> Result<Self, Error>
where where
Self: Sized; 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 where
Self: Sized; Self: Sized;
} }
@ -170,6 +173,12 @@ pub enum SearchType {
Url, Url,
} }
#[derive(EnumString, ToString, Debug, Serialize, Deserialize)]
pub enum ReportType {
Comment,
Post,
}
pub fn fuzzy_search(q: &str) -> String { pub fn fuzzy_search(q: &str) -> String {
let replaced = q.replace(" ", "%"); let replaced = q.replace(" ", "%");
format!("%{}%", replaced) format!("%{}%", replaced)

View File

@ -1,32 +1,25 @@
use diesel::{PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods, insert_into, update}; use diesel::{dsl::*, pg::Pg, result::Error, *};
use diesel::pg::Pg;
use diesel::result::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{limit_and_offset, MaybeOptional, schema::post_report, post::Post, Reportable, naive_now};
limit_and_offset,
MaybeOptional,
schema::post_report,
post::Post,
Reportable,
};
table! { table! {
post_report_view (id) { post_report_view (id) {
id -> Uuid, id -> Int4,
time -> Timestamp, creator_id -> Int4,
reason -> Nullable<Text>, post_id -> Int4,
resolved -> Bool, post_name -> Varchar,
user_id -> Int4, post_url -> Nullable<Text>,
post_id -> Int4, post_body -> Nullable<Text>,
post_name -> Varchar, reason -> Text,
post_url -> Nullable<Text>, resolved -> Bool,
post_body -> Nullable<Text>, resolver_id -> Nullable<Int4>,
post_time -> Timestamp, published -> Timestamp,
community_id -> Int4, updated -> Nullable<Timestamp>,
user_name -> Varchar, community_id -> Int4,
creator_id -> Int4, creator_name -> Varchar,
creator_name -> Varchar, post_creator_id -> Int4,
post_creator_name -> Varchar,
} }
} }
@ -34,30 +27,28 @@ table! {
#[belongs_to(Post)] #[belongs_to(Post)]
#[table_name = "post_report"] #[table_name = "post_report"]
pub struct PostReport { pub struct PostReport {
pub id: uuid::Uuid, pub id: i32,
pub time: chrono::NaiveDateTime, pub creator_id: i32,
pub reason: Option<String>,
pub resolved: bool,
pub user_id: i32,
pub post_id: i32, pub post_id: i32,
pub post_name: String, pub post_name: String,
pub post_url: Option<String>, pub post_url: Option<String>,
pub post_body: 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)] #[derive(Insertable, AsChangeset, Clone)]
#[table_name = "post_report"] #[table_name = "post_report"]
pub struct PostReportForm { pub struct PostReportForm {
pub time: Option<chrono::NaiveDateTime>, pub creator_id: i32,
pub reason: Option<String>,
pub resolved: Option<bool>,
pub user_id: i32,
pub post_id: i32, pub post_id: i32,
pub post_name: String, pub post_name: String,
pub post_url: Option<String>, pub post_url: Option<String>,
pub post_body: Option<String>, pub post_body: Option<String>,
pub post_time: chrono::NaiveDateTime, pub reason: String,
} }
impl Reportable<PostReportForm> for PostReport { impl Reportable<PostReportForm> for PostReport {
@ -68,37 +59,52 @@ impl Reportable<PostReportForm> for PostReport {
.get_result::<Self>(conn) .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::*; use crate::schema::post_report::dsl::*;
update(post_report.find(report_id)) 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) .execute(conn)
} }
} }
#[derive( #[derive(
Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
)] )]
#[table_name = "post_report_view"] #[table_name = "post_report_view"]
pub struct PostReportView { pub struct PostReportView {
pub id: uuid::Uuid, pub id: i32,
pub time: chrono::NaiveDateTime, pub creator_id: i32,
pub reason: Option<String>,
pub resolved: bool,
pub user_id: i32,
pub post_id: i32, pub post_id: i32,
pub post_name: String, pub post_name: String,
pub post_url: Option<String>, pub post_url: Option<String>,
pub post_body: 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 community_id: i32,
pub user_name: String,
pub creator_id: i32,
pub creator_name: String, pub creator_name: String,
pub post_creator_id: i32,
pub post_creator_name: String,
} }
impl PostReportView { 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::*; use super::post_report::post_report_view::dsl::*;
post_report_view post_report_view
.filter(id.eq(report_id)) .filter(id.eq(report_id))
@ -167,7 +173,7 @@ impl<'a> PostReportQueryBuilder<'a> {
let (limit, offset) = limit_and_offset(self.page, self.limit); let (limit, offset) = limit_and_offset(self.page, self.limit);
query query
.order_by(time.desc()) .order_by(published.desc())
.limit(limit) .limit(limit)
.offset(offset) .offset(offset)
.load::<PostReportView>(self.conn) .load::<PostReportView>(self.conn)

View File

@ -83,14 +83,15 @@ table! {
table! { table! {
comment_report (id) { comment_report (id) {
id -> Uuid, id -> Int4,
time -> Timestamp, creator_id -> Int4,
reason -> Nullable<Text>,
resolved -> Bool,
user_id -> Int4,
comment_id -> Int4, comment_id -> Int4,
comment_text -> Text, comment_text -> Text,
comment_time -> Timestamp, reason -> Text,
resolved -> Bool,
resolver_id -> Nullable<Int4>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
} }
} }
@ -385,16 +386,17 @@ table! {
table! { table! {
post_report (id) { post_report (id) {
id -> Uuid, id -> Int4,
time -> Timestamp, creator_id -> Int4,
reason -> Nullable<Text>,
resolved -> Bool,
user_id -> Int4,
post_id -> Int4, post_id -> Int4,
post_name -> Varchar, post_name -> Varchar,
post_url -> Nullable<Text>, post_url -> Nullable<Text>,
post_body -> Nullable<Text>, post_body -> Nullable<Text>,
post_time -> Timestamp, reason -> Text,
resolved -> Bool,
resolver_id -> Nullable<Int4>,
published -> Timestamp,
updated -> Nullable<Timestamp>,
} }
} }
@ -516,7 +518,6 @@ joinable!(comment_like -> comment (comment_id));
joinable!(comment_like -> post (post_id)); joinable!(comment_like -> post (post_id));
joinable!(comment_like -> user_ (user_id)); joinable!(comment_like -> user_ (user_id));
joinable!(comment_report -> comment (comment_id)); joinable!(comment_report -> comment (comment_id));
joinable!(comment_report -> user_ (user_id));
joinable!(comment_saved -> comment (comment_id)); joinable!(comment_saved -> comment (comment_id));
joinable!(comment_saved -> user_ (user_id)); joinable!(comment_saved -> user_ (user_id));
joinable!(community -> category (category_id)); joinable!(community -> category (category_id));
@ -547,7 +548,6 @@ joinable!(post_like -> user_ (user_id));
joinable!(post_read -> post (post_id)); joinable!(post_read -> post (post_id));
joinable!(post_read -> user_ (user_id)); joinable!(post_read -> user_ (user_id));
joinable!(post_report -> post (post_id)); joinable!(post_report -> post (post_id));
joinable!(post_report -> user_ (user_id));
joinable!(post_saved -> post (post_id)); joinable!(post_saved -> post (post_id));
joinable!(post_saved -> user_ (user_id)); joinable!(post_saved -> user_ (user_id));
joinable!(site -> user_ (creator_id)); joinable!(site -> user_ (creator_id));
@ -556,38 +556,38 @@ joinable!(user_mention -> comment (comment_id));
joinable!(user_mention -> user_ (recipient_id)); joinable!(user_mention -> user_ (recipient_id));
allow_tables_to_appear_in_same_query!( allow_tables_to_appear_in_same_query!(
activity, activity,
category, category,
comment, comment,
comment_aggregates_fast, comment_aggregates_fast,
comment_like, comment_like,
comment_report, comment_report,
comment_saved, comment_saved,
community, community,
community_aggregates_fast, community_aggregates_fast,
community_follower, community_follower,
community_moderator, community_moderator,
community_user_ban, community_user_ban,
mod_add, mod_add,
mod_add_community, mod_add_community,
mod_ban, mod_ban,
mod_ban_from_community, mod_ban_from_community,
mod_lock_post, mod_lock_post,
mod_remove_comment, mod_remove_comment,
mod_remove_community, mod_remove_community,
mod_remove_post, mod_remove_post,
mod_sticky_post, mod_sticky_post,
password_reset_request, password_reset_request,
post, post,
post_aggregates_fast, post_aggregates_fast,
post_like, post_like,
post_read, post_read,
post_report, post_report,
post_saved, post_saved,
private_message, private_message,
site, site,
user_, user_,
user_ban, user_ban,
user_fast, user_fast,
user_mention, user_mention,
); );

View File

@ -17,4 +17,3 @@ diesel = "1.4"
actix-web = { version = "3.0" } actix-web = { version = "3.0" }
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
serde_json = { version = "1.0", features = ["preserve_order"]} serde_json = { version = "1.0", features = ["preserve_order"]}
uuid = { version = "0.6.5", features = ["serde", "v4"] }

View File

@ -1,57 +1,31 @@
use lemmy_db::{ use lemmy_db::{comment_report::CommentReportView, post_report::PostReportView};
comment_report::CommentReportView,
post_report::PostReportView,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct CreateCommentReport { pub struct CreateReport {
pub comment: i32, pub report_type: String,
pub reason: Option<String>, pub entity_id: i32,
pub reason: String,
pub auth: String, pub auth: String,
} }
#[derive(Serialize, Deserialize, Clone)] #[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, pub success: bool,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct ListCommentReports { pub struct ListReports {
pub page: Option<i64>, pub page: Option<i64>,
pub limit: Option<i64>, pub limit: Option<i64>,
pub community: i32, pub community: i32,
pub auth: String, pub auth: String,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ListCommentReportResponse { pub struct ListReportsResponse {
pub reports: Vec<CommentReportView>, pub posts: Vec<PostReportView>,
} pub comments: Vec<CommentReportView>,
#[derive(Serialize, Deserialize, Debug)]
pub struct ListPostReports {
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, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -60,7 +34,7 @@ pub struct GetReportCount {
pub auth: String, pub auth: String,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct GetReportCountResponse { pub struct GetReportCountResponse {
pub community: i32, pub community: i32,
pub comment_reports: usize, pub comment_reports: usize,
@ -68,25 +42,16 @@ pub struct GetReportCountResponse {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct ResolveCommentReport { pub struct ResolveReport {
pub report: uuid::Uuid, pub report_type: String,
pub report_id: i32,
pub resolved: bool,
pub auth: String, pub auth: String,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ResolveCommentReportResponse { pub struct ResolveReportResponse {
pub report: uuid::Uuid, pub report_type: String,
pub resolved: bool, pub report_id: i32,
}
#[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,
pub resolved: bool, pub resolved: bool,
} }

View File

@ -97,9 +97,6 @@ pub enum UserOperation {
MarkCommentAsRead, MarkCommentAsRead,
SaveComment, SaveComment,
CreateCommentLike, CreateCommentLike,
CreateCommentReport,
ListCommentReports,
ResolveCommentReport,
GetPosts, GetPosts,
CreatePostLike, CreatePostLike,
EditPost, EditPost,
@ -108,9 +105,10 @@ pub enum UserOperation {
LockPost, LockPost,
StickyPost, StickyPost,
SavePost, SavePost,
CreatePostReport, CreateReport,
ListPostReports, ResolveReport,
ResolvePostReport, ListReports,
GetReportCount,
EditCommunity, EditCommunity,
DeleteCommunity, DeleteCommunity,
RemoveCommunity, RemoveCommunity,
@ -121,7 +119,6 @@ pub enum UserOperation {
GetUserMentions, GetUserMentions,
MarkUserMentionAsRead, MarkUserMentionAsRead,
GetModlog, GetModlog,
GetReportCount,
BanFromCommunity, BanFromCommunity,
AddModToCommunity, AddModToCommunity,
CreateSite, CreateSite,

View File

@ -2,4 +2,3 @@ drop view comment_report_view;
drop view post_report_view; drop view post_report_view;
drop table comment_report; drop table comment_report;
drop table post_report; drop table post_report;
drop extension "uuid-ossp";

View File

@ -1,51 +1,51 @@
create extension "uuid-ossp";
create table comment_report ( create table comment_report (
id uuid primary key default uuid_generate_v4(), id serial primary key,
time timestamp not null default now(), creator_id int references user_ on update cascade on delete cascade not null, -- user reporting comment
reason text,
resolved bool not null default false,
user_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_id int references comment on update cascade on delete cascade not null, -- comment being reported
comment_text text not null, comment_text text not null,
comment_time timestamp not null, reason text not null,
unique(comment_id, user_id) -- users should only be able to report a comment once 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 ( create table post_report (
id uuid primary key default uuid_generate_v4(), id serial primary key,
time timestamp not null default now(), creator_id int references user_ on update cascade on delete cascade not null, -- user reporting post
reason text,
resolved bool not null default false,
user_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_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_url text,
post_body text, post_body text,
post_time timestamp not null, reason text not null,
unique(post_id, user_id) -- users should only be able to report a post once 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 create or replace view comment_report_view as
select cr.*, select cr.*,
c.post_id, c.post_id,
p.community_id, p.community_id,
f.name as user_name, f.name as creator_name,
u.id as creator_id, u.id as comment_creator_id,
u.name as creator_name u.name as comment_creator_name
from comment_report cr from comment_report cr
left join comment c on c.id = cr.comment_id left join comment c on c.id = cr.comment_id
left join post p on p.id = c.post_id left join post p on p.id = c.post_id
left join user_ u on u.id = c.creator_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 create or replace view post_report_view as
select pr.*, select pr.*,
p.community_id, p.community_id,
f.name as user_name, f.name as creator_name,
u.id as creator_id, u.id as post_creator_id,
u.name as creator_name u.name as post_creator_name
from post_report pr from post_report pr
left join post p on p.id = pr.post_id left join post p on p.id = pr.post_id
left join user_ u on u.id = p.creator_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;

View File

@ -58,9 +58,6 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
.route("/ban_user", web::post().to(route_post::<BanFromCommunity>)) .route("/ban_user", web::post().to(route_post::<BanFromCommunity>))
.route("/mod", web::post().to(route_post::<AddModToCommunity>)) .route("/mod", web::post().to(route_post::<AddModToCommunity>))
.route("/join", web::post().to(route_post::<CommunityJoin>)) .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 // Post
.service( .service(
@ -83,12 +80,10 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
.route("/like", web::post().to(route_post::<CreatePostLike>)) .route("/like", web::post().to(route_post::<CreatePostLike>))
.route("/save", web::put().to(route_post::<SavePost>)) .route("/save", web::put().to(route_post::<SavePost>))
.route("/join", web::post().to(route_post::<PostJoin>)) .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 // Comment
.service( .service(
web::scope("/comment") web::scope("/comment")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", web::post().to(route_post::<CreateComment>)) .route("", web::post().to(route_post::<CreateComment>))
.route("", web::put().to(route_post::<EditComment>)) .route("", web::put().to(route_post::<EditComment>))
@ -101,8 +96,6 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
.route("/like", web::post().to(route_post::<CreateCommentLike>)) .route("/like", web::post().to(route_post::<CreateCommentLike>))
.route("/save", web::put().to(route_post::<SaveComment>)) .route("/save", web::put().to(route_post::<SaveComment>))
.route("/list", web::get().to(route_get::<GetComments>)) .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 // Private Message
.service( .service(
@ -177,6 +170,15 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
web::resource("/admin/add") web::resource("/admin/add")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route(web::post().to(route_post::<AddAdmin>)), .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>))
), ),
); );
} }