feat: configurable auto block
This commit is contained in:
@@ -67,7 +67,7 @@ pub async fn get_attention(user: CurrentUser, db: Db, rconn: RdsConn) -> JsonAPI
|
||||
let mut ids = Attention::init(&user.namehash, &rconn).all().await?;
|
||||
ids.sort_by_key(|x| -x);
|
||||
let ps = Post::get_multi(&db, &rconn, &ids).await?;
|
||||
let ps_data = ps2outputs(&ps, &user, &db, &rconn).await;
|
||||
let ps_data = ps2outputs(&ps, &user, &db, &rconn).await?;
|
||||
|
||||
code0!(ps_data)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::api::{APIError, CurrentUser, JsonAPI, PolicyError::*, UGC};
|
||||
use crate::cache::BlockDictCache;
|
||||
use crate::db_conn::Db;
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::models::*;
|
||||
@@ -41,6 +42,7 @@ pub async fn c2output<'r>(
|
||||
p: &'r Post,
|
||||
cs: &Vec<Comment>,
|
||||
user: &CurrentUser,
|
||||
cached_block_dict: &HashMap<String, bool>,
|
||||
rconn: &RdsConn,
|
||||
) -> Vec<CommentOutput> {
|
||||
let mut hash2id = HashMap::<&String, i32>::from([(&p.author_hash, 0)]);
|
||||
@@ -56,10 +58,7 @@ pub async fn c2output<'r>(
|
||||
if c.is_deleted {
|
||||
None
|
||||
} else {
|
||||
let is_blocked =
|
||||
BlockedUsers::check_blocked(rconn, user.id, &user.namehash, &c.author_hash)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
let is_blocked = cached_block_dict[&c.author_hash];
|
||||
let can_view = user.is_admin
|
||||
|| (!is_blocked && user.id.is_some() || user.namehash.eq(&c.author_hash));
|
||||
Some(CommentOutput {
|
||||
@@ -72,7 +71,10 @@ pub async fn c2output<'r>(
|
||||
create_time: c.create_time.timestamp(),
|
||||
is_blocked: is_blocked,
|
||||
blocked_count: if user.is_admin {
|
||||
BlockCounter::get_count(rconn, &c.author_hash).await.ok()
|
||||
BlockCounter::get_count(rconn, &c.author_hash)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
@@ -94,7 +96,11 @@ pub async fn get_comment(pid: i32, user: CurrentUser, db: Db, rconn: RdsConn) ->
|
||||
return Err(APIError::PcError(IsDeleted));
|
||||
}
|
||||
let cs = p.get_comments(&db, &rconn).await?;
|
||||
let data = c2output(&p, &cs, &user, &rconn).await;
|
||||
let hash_list = cs.iter().map(|c| &c.author_hash).collect();
|
||||
let cached_block_dict = BlockDictCache::init(&user.namehash, p.id, &rconn)
|
||||
.get_or_create(&user, &hash_list)
|
||||
.await?;
|
||||
let data = c2output(&p, &cs, &user, &cached_block_dict, &rconn).await;
|
||||
|
||||
Ok(json!({
|
||||
"code": 0,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::models::*;
|
||||
use crate::random_hasher::RandomHasher;
|
||||
use crate::rds_conn::RdsConn;
|
||||
use crate::rds_models::BannedUsers;
|
||||
use crate::rds_models::*;
|
||||
use crate::schema;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use rocket::http::Status;
|
||||
@@ -52,10 +52,11 @@ pub fn catch_403_error() -> &'static str {
|
||||
}
|
||||
|
||||
pub struct CurrentUser {
|
||||
id: Option<i32>, // tmp user has no id, only for block
|
||||
pub id: Option<i32>, // tmp user has no id, only for block
|
||||
namehash: String,
|
||||
is_admin: bool,
|
||||
custom_title: String,
|
||||
pub auto_block_rank: u8,
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
@@ -91,7 +92,12 @@ impl<'r> FromRequest<'r> for CurrentUser {
|
||||
} else {
|
||||
Outcome::Success(CurrentUser {
|
||||
id: id,
|
||||
custom_title: format!("title todo: {}", &nh),
|
||||
custom_title: CustomTitle::get(&rconn, &nh)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default(),
|
||||
auto_block_rank: AutoBlockRank::get(&rconn, &nh).await.unwrap_or(2),
|
||||
namehash: nh,
|
||||
is_admin: is_admin,
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::api::{CurrentUser, JsonAPI, PolicyError::*, UGC};
|
||||
use crate::api::{APIError, CurrentUser, JsonAPI, PolicyError::*, UGC};
|
||||
use crate::cache::*;
|
||||
use crate::db_conn::Db;
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::models::*;
|
||||
@@ -72,7 +73,6 @@ pub async fn delete(di: Form<DeleteInput>, user: CurrentUser, db: Db, rconn: Rds
|
||||
.create(&rconn)
|
||||
.await?;
|
||||
BannedUsers::add(&rconn, &author_hash).await?;
|
||||
DangerousUser::add(&rconn, &author_hash).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,28 +128,39 @@ pub async fn block(bi: Form<BlockInput>, user: CurrentUser, db: Db, rconn: RdsCo
|
||||
|
||||
let mut blk = BlockedUsers::init(user.id.ok_or_else(|| NotAllowed)?, &rconn);
|
||||
|
||||
let pid;
|
||||
let nh_to_block = match bi.content_type.as_str() {
|
||||
"post" => Post::get(&db, &rconn, bi.id).await?.author_hash,
|
||||
"comment" => Comment::get(&db, bi.id).await?.author_hash,
|
||||
_ => Err(NotAllowed)?,
|
||||
"post" => {
|
||||
let p = Post::get(&db, &rconn, bi.id).await?;
|
||||
pid = p.id;
|
||||
p.author_hash
|
||||
}
|
||||
"comment" => {
|
||||
let c = Comment::get(&db, bi.id).await?;
|
||||
pid = c.post_id;
|
||||
c.author_hash
|
||||
}
|
||||
_ => return Err(APIError::PcError(NotAllowed)),
|
||||
};
|
||||
|
||||
if nh_to_block.eq(&user.namehash) {
|
||||
Err(NotAllowed)?;
|
||||
}
|
||||
|
||||
blk.add(&nh_to_block).await?;
|
||||
let curr = BlockCounter::count_incr(&rconn, &nh_to_block).await?;
|
||||
let curr = if blk.add(&nh_to_block).await? > 0 {
|
||||
BlockCounter::count_incr(&rconn, &nh_to_block).await?
|
||||
} else {
|
||||
114514
|
||||
};
|
||||
|
||||
if curr >= BLOCK_THRESHOLD || user.is_admin {
|
||||
DangerousUser::add(&rconn, &nh_to_block).await?;
|
||||
}
|
||||
BlockDictCache::init(&user.namehash, pid, &rconn)
|
||||
.clear()
|
||||
.await?;
|
||||
|
||||
Ok(json!({
|
||||
"code": 0,
|
||||
"data": {
|
||||
"curr": curr,
|
||||
"threshold": BLOCK_THRESHOLD,
|
||||
},
|
||||
}))
|
||||
}
|
||||
@@ -168,3 +179,18 @@ pub async fn set_title(ti: Form<TitleInput>, user: CurrentUser, rconn: RdsConn)
|
||||
Err(TitleUsed)?
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct AutoBlockInput {
|
||||
rank: u8,
|
||||
}
|
||||
|
||||
#[post("/auto_block", data = "<ai>")]
|
||||
pub async fn set_auto_block(
|
||||
ai: Form<AutoBlockInput>,
|
||||
user: CurrentUser,
|
||||
rconn: RdsConn,
|
||||
) -> JsonAPI {
|
||||
AutoBlockRank::set(&rconn, &user.namehash, ai.rank).await?;
|
||||
code0!()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::api::comment::{c2output, CommentOutput};
|
||||
use crate::api::vote::get_poll_dict;
|
||||
use crate::api::{CurrentUser, JsonAPI, PolicyError::*, UGC};
|
||||
use crate::api::{CurrentUser, JsonAPI, PolicyError::*, API, UGC};
|
||||
use crate::cache::*;
|
||||
use crate::db_conn::Db;
|
||||
use crate::libs::diesel_logger::LoggingConnection;
|
||||
use crate::models::*;
|
||||
@@ -9,7 +10,7 @@ use crate::rds_models::*;
|
||||
use crate::schema;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use rocket::form::Form;
|
||||
use rocket::futures::future;
|
||||
use rocket::futures::future::{self, OptionFuture};
|
||||
use rocket::serde::{
|
||||
json::{json, Value},
|
||||
Serialize,
|
||||
@@ -62,47 +63,48 @@ pub struct CwInput {
|
||||
cw: String,
|
||||
}
|
||||
|
||||
async fn p2output(p: &Post, user: &CurrentUser, db: &Db, rconn: &RdsConn) -> PostOutput {
|
||||
let is_blocked = BlockedUsers::check_blocked(rconn, user.id, &user.namehash, &p.author_hash)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
async fn p2output(p: &Post, user: &CurrentUser, db: &Db, rconn: &RdsConn) -> API<PostOutput> {
|
||||
let comments: Option<Vec<Comment>> = if p.n_comments < 5 {
|
||||
Some(p.get_comments(db, rconn).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let hash_list = comments
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|c| &c.author_hash)
|
||||
.chain(std::iter::once(&p.author_hash))
|
||||
.collect();
|
||||
//dbg!(&hash_list);
|
||||
let cached_block_dict = BlockDictCache::init(&user.namehash, p.id, rconn)
|
||||
.get_or_create(&user, &hash_list)
|
||||
.await?;
|
||||
let is_blocked = cached_block_dict[&p.author_hash];
|
||||
let can_view =
|
||||
user.is_admin || (!is_blocked && user.id.is_some() || user.namehash.eq(&p.author_hash));
|
||||
PostOutput {
|
||||
Ok(PostOutput {
|
||||
pid: p.id,
|
||||
text: (if can_view { &p.content } else { "" }).to_string(),
|
||||
cw: (!p.cw.is_empty()).then(|| p.cw.to_string()),
|
||||
text: can_view.then(|| p.content.clone()).unwrap_or_default(),
|
||||
cw: (!p.cw.is_empty()).then(|| p.cw.clone()),
|
||||
n_attentions: p.n_attentions,
|
||||
n_comments: p.n_comments,
|
||||
create_time: p.create_time.timestamp(),
|
||||
last_comment_time: p.last_comment_time.timestamp(),
|
||||
allow_search: p.allow_search,
|
||||
author_title: (!p.author_title.is_empty()).then(|| p.author_title.to_string()),
|
||||
author_title: (!p.author_title.is_empty()).then(|| p.author_title.clone()),
|
||||
is_tmp: p.is_tmp,
|
||||
is_reported: user.is_admin.then(|| p.is_reported),
|
||||
comments: if p.n_comments > 50 {
|
||||
None
|
||||
} else {
|
||||
// 单个洞还有查询评论的接口,这里挂了不用报错
|
||||
Some(
|
||||
c2output(
|
||||
p,
|
||||
&p.get_comments(db, rconn).await.unwrap_or(vec![]),
|
||||
user,
|
||||
rconn,
|
||||
)
|
||||
.await,
|
||||
)
|
||||
},
|
||||
comments: OptionFuture::from(
|
||||
comments
|
||||
.map(|cs| async move { c2output(p, &cs, user, &cached_block_dict, rconn).await }),
|
||||
)
|
||||
.await,
|
||||
can_del: p.check_permission(user, "wd").is_ok(),
|
||||
attention: Attention::init(&user.namehash, &rconn)
|
||||
.has(p.id)
|
||||
.await
|
||||
.unwrap_or_default(),
|
||||
attention: Attention::init(&user.namehash, &rconn).has(p.id).await?,
|
||||
hot_score: user.is_admin.then(|| p.hot_score),
|
||||
is_blocked: is_blocked,
|
||||
blocked_count: if user.is_admin {
|
||||
BlockCounter::get_count(rconn, &p.author_hash).await.ok()
|
||||
BlockCounter::get_count(rconn, &p.author_hash).await?
|
||||
} else {
|
||||
None
|
||||
},
|
||||
@@ -116,7 +118,7 @@ async fn p2output(p: &Post, user: &CurrentUser, db: &Db, rconn: &RdsConn) -> Pos
|
||||
likenum: p.n_attentions,
|
||||
reply: p.n_comments,
|
||||
blocked: is_blocked,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn ps2outputs(
|
||||
@@ -124,10 +126,10 @@ pub async fn ps2outputs(
|
||||
user: &CurrentUser,
|
||||
db: &Db,
|
||||
rconn: &RdsConn,
|
||||
) -> Vec<PostOutput> {
|
||||
future::join_all(
|
||||
) -> API<Vec<PostOutput>> {
|
||||
future::try_join_all(
|
||||
ps.iter()
|
||||
.map(|p| async { p2output(p, &user, &db, &rconn).await }),
|
||||
.map(|p| async { Ok(p2output(p, &user, &db, &rconn).await?) }),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -137,7 +139,7 @@ pub async fn get_one(pid: i32, user: CurrentUser, db: Db, rconn: RdsConn) -> Jso
|
||||
let p = Post::get(&db, &rconn, pid).await?;
|
||||
p.check_permission(&user, "ro")?;
|
||||
Ok(json!({
|
||||
"data": p2output(&p, &user,&db, &rconn).await,
|
||||
"data": p2output(&p, &user,&db, &rconn).await?,
|
||||
"code": 0,
|
||||
}))
|
||||
}
|
||||
@@ -155,11 +157,12 @@ pub async fn get_list(
|
||||
let page_size = 25;
|
||||
let start = (page - 1) * page_size;
|
||||
let ps = Post::gets_by_page(&db, &rconn, order_mode, start.into(), page_size.into()).await?;
|
||||
let ps_data = ps2outputs(&ps, &user, &db, &rconn).await;
|
||||
let ps_data = ps2outputs(&ps, &user, &db, &rconn).await?;
|
||||
Ok(json!({
|
||||
"data": ps_data,
|
||||
"count": ps_data.len(),
|
||||
"custom_title": CustomTitle::get(&rconn, &user.namehash).await?,
|
||||
"custom_title": user.custom_title,
|
||||
"auto_block_rank": user.auto_block_rank,
|
||||
"code": 0
|
||||
}))
|
||||
}
|
||||
@@ -177,12 +180,7 @@ pub async fn publish_post(
|
||||
content: poi.text.to_string(),
|
||||
cw: poi.cw.to_string(),
|
||||
author_hash: user.namehash.to_string(),
|
||||
author_title: (if poi.use_title.is_some() {
|
||||
CustomTitle::get(&rconn, &user.namehash).await?
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
author_title: poi.use_title.map(|_| user.custom_title).unwrap_or_default(),
|
||||
is_tmp: user.id.is_none(),
|
||||
n_attentions: 1,
|
||||
allow_search: poi.allow_search.is_some(),
|
||||
@@ -213,7 +211,7 @@ pub async fn edit_cw(cwi: Form<CwInput>, user: CurrentUser, db: Db, rconn: RdsCo
|
||||
pub async fn get_multi(pids: Vec<i32>, user: CurrentUser, db: Db, rconn: RdsConn) -> JsonAPI {
|
||||
user.id.ok_or_else(|| YouAreTmp)?;
|
||||
let ps = Post::get_multi(&db, &rconn, &pids).await?;
|
||||
let ps_data = ps2outputs(&ps, &user, &db, &rconn).await;
|
||||
let ps_data = ps2outputs(&ps, &user, &db, &rconn).await?;
|
||||
|
||||
Ok(json!({
|
||||
"code": 0,
|
||||
|
||||
@@ -36,7 +36,7 @@ pub async fn search(
|
||||
)
|
||||
.await?
|
||||
};
|
||||
let ps_data = ps2outputs(&ps, &user, &db, &rconn).await;
|
||||
let ps_data = ps2outputs(&ps, &user, &db, &rconn).await?;
|
||||
Ok(json!({
|
||||
"data": ps_data,
|
||||
"count": ps_data.len(),
|
||||
|
||||
Reference in New Issue
Block a user