feat: add comment & operation & UGC trait
This commit is contained in:
@@ -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
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -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
32
src/api/operation.rs
Normal 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
|
||||
}))
|
||||
}
|
||||
@@ -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}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user