feat: add comment & operation & UGC trait

This commit is contained in:
2022-03-18 02:58:19 +08:00
parent 3fb47c7fc2
commit e87d8acb7c
7 changed files with 240 additions and 36 deletions

View File

@@ -1,13 +1,22 @@
use crate::api::{APIError, CurrentUser, PolicyError::*, API};
use crate::db_conn::DbConn;
use crate::models::*;
use chrono::NaiveDateTime;
use rocket::form::Form;
use rocket::serde::{
json::{json, Value},
Serialize,
};
use crate::db_conn::DbConn;
use std::collections::HashMap;
#[derive(FromForm)]
pub struct CommentInput<'r> {
pid: i32,
#[field(validate = len(1..4097))]
text: &'r str,
use_title: Option<i8>,
}
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct CommentOutput {
@@ -32,12 +41,17 @@ pub fn c2output(p: &Post, cs: &Vec<Comment>, user: &CurrentUser) -> Vec<CommentO
x
}
};
if c.is_deleted {
if false {
// TODO: block
None
} else {
Some(CommentOutput {
cid: c.id,
text: c.content.to_string(),
text: if c.is_deleted {
"[已删除]".to_string()
} else {
c.content.to_string()
},
can_del: user.is_admin || c.author_hash == user.namehash,
name_id: name_id,
create_time: c.create_time,
@@ -63,3 +77,22 @@ pub fn get_comment(pid: i32, user: CurrentUser, conn: DbConn) -> API<Value> {
"likenum": p.n_likes,
}))
}
#[post("/docomment", data = "<ci>")]
pub fn add_comment(ci: Form<CommentInput>, user: CurrentUser, conn: DbConn) -> API<Value> {
let p = Post::get(&conn, ci.pid).map_err(APIError::from_db)?;
Comment::create(
&conn,
NewComment {
content: &ci.text,
author_hash: &user.namehash,
author_title: "",
post_id: ci.pid,
},
)
.map_err(APIError::from_db)?;
p.after_add_comment(&conn).map_err(APIError::from_db)?;
Ok(json!({
"code": 0
}))
}

View File

@@ -1,10 +1,10 @@
use crate::db_conn::{Conn, DbPool};
use crate::models::*;
use crate::random_hasher::RandomHasher;
use rocket::http::Status;
use rocket::request::{FromRequest, Request, Outcome};
use rocket::request::{FromRequest, Outcome, Request};
use rocket::response::{self, Responder};
use rocket::serde::json::json;
use crate::db_conn::DbPool;
#[catch(401)]
pub fn catch_401_error() -> &'static str {
@@ -93,6 +93,75 @@ impl<'r> Responder<'r, 'static> for APIError {
}
}
pub trait UGC {
fn get_author_hash(&self) -> &str;
fn get_is_deleted(&self) -> bool;
fn get_is_reported(&self) -> bool;
fn extra_delete_condition(&self) -> bool;
fn do_set_deleted(&self, conn: &Conn) -> API<()>;
fn check_permission(&self, user: &CurrentUser, mode: &str) -> API<()> {
if user.is_admin {
return Ok(());
}
if mode.contains('r') && self.get_is_deleted() {
return Err(APIError::PcError(PolicyError::IsDeleted));
}
if mode.contains('o') && self.get_is_reported() {
return Err(APIError::PcError(PolicyError::IsReported));
}
if mode.contains('w') && self.get_author_hash() != user.namehash {
return Err(APIError::PcError(PolicyError::NotAllowed));
}
if mode.contains('d') && !self.extra_delete_condition() {
return Err(APIError::PcError(PolicyError::NotAllowed));
}
Ok(())
}
fn soft_delete(&self, user: &CurrentUser, conn: &Conn) -> API<()> {
self.check_permission(user, "rwd")?;
self.do_set_deleted(conn)?;
Ok(())
}
}
impl UGC for Post {
fn get_author_hash(&self) -> &str {
&self.author_hash
}
fn get_is_reported(&self) -> bool {
self.is_reported
}
fn get_is_deleted(&self) -> bool {
self.is_deleted
}
fn extra_delete_condition(&self) -> bool {
self.n_comments == 0
}
fn do_set_deleted(&self, conn: &Conn) -> API<()> {
self.set_deleted(conn).map_err(APIError::from_db)
}
}
impl UGC for Comment {
fn get_author_hash(&self) -> &str {
&self.author_hash
}
fn get_is_reported(&self) -> bool {
false
}
fn get_is_deleted(&self) -> bool {
self.is_deleted
}
fn extra_delete_condition(&self) -> bool {
true
}
fn do_set_deleted(&self, conn: &Conn) -> API<()> {
self.set_deleted(conn).map_err(APIError::from_db)
}
}
macro_rules! look {
($s:expr) => {
format!("{}...{}", &$s[..2], &$s[$s.len() - 2..])
@@ -102,5 +171,6 @@ macro_rules! look {
pub type API<T> = Result<T, APIError>;
pub mod comment;
pub mod operation;
pub mod post;
pub mod systemlog;

32
src/api/operation.rs Normal file
View File

@@ -0,0 +1,32 @@
use crate::api::{APIError, CurrentUser, PolicyError::*, API, UGC};
use crate::db_conn::DbConn;
use crate::models::*;
use rocket::form::Form;
use rocket::serde::json::{json, Value};
#[derive(FromForm)]
pub struct DeleteInput<'r> {
#[field(name = "type")]
id_type: &'r str,
id: i32,
note: &'r str,
}
#[post("/delete", data = "<di>")]
pub fn delete(di: Form<DeleteInput>, user: CurrentUser, conn: DbConn) -> API<Value> {
match di.id_type {
"cid" => {
let c = Comment::get(&conn, di.id).map_err(APIError::from_db)?;
c.soft_delete(&user, &conn)?;
}
"pid" => {
let p = Post::get(&conn, di.id).map_err(APIError::from_db)?;
p.soft_delete(&user, &conn)?;
}
_ => return Err(APIError::PcError(NotAllowed)),
}
Ok(json!({
"code": 0
}))
}

View File

@@ -1,5 +1,5 @@
use crate::api::comment::{c2output, CommentOutput};
use crate::api::{APIError, CurrentUser, PolicyError::*, API};
use crate::api::{APIError, CurrentUser, PolicyError::*, API, UGC};
use crate::db_conn::DbConn;
use crate::models::*;
use chrono::NaiveDateTime;
@@ -89,14 +89,7 @@ fn p2output(p: &Post, user: &CurrentUser, conn: &DbConn) -> PostOutput {
#[get("/getone?<pid>")]
pub fn get_one(pid: i32, user: CurrentUser, conn: DbConn) -> API<Value> {
let p = Post::get(&conn, pid).map_err(APIError::from_db)?;
if !user.is_admin {
if p.is_reported {
return Err(APIError::PcError(IsReported));
}
if p.is_deleted {
return Err(APIError::PcError(IsDeleted));
}
}
p.check_permission(&user, "ro")?;
Ok(json!({
"data": p2output(&p, &user, &conn),
"code": 0,
@@ -106,8 +99,7 @@ pub fn get_one(pid: i32, user: CurrentUser, conn: DbConn) -> API<Value> {
#[get("/getlist?<p>&<order_mode>")]
pub fn get_list(p: Option<u32>, order_mode: u8, user: CurrentUser, conn: DbConn) -> API<Value> {
let page = p.unwrap_or(1);
let ps = Post::gets_by_page(&conn, order_mode, page, 25, user.is_admin)
.map_err(APIError::from_db)?;
let ps = Post::gets_by_page(&conn, order_mode, page, 25).map_err(APIError::from_db)?;
let ps_data = ps
.iter()
.map(|p| p2output(p, &user, &conn))
@@ -145,6 +137,7 @@ pub fn edit_cw(cwi: Form<CwInput>, user: CurrentUser, conn: DbConn) -> API<Value
if !(user.is_admin || p.author_hash == user.namehash) {
return Err(APIError::PcError(NotAllowed));
}
p.check_permission(&user, "w")?;
_ = p.update_cw(&conn, cwi.cw);
Ok(json!({"code": 0}))
}