feat: configurable auto block

This commit is contained in:
2022-04-10 15:34:10 +08:00
parent 728887e1e2
commit 47a6e51a2a
10 changed files with 195 additions and 101 deletions

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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,
})

View File

@@ -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!()
}

View File

@@ -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,

View File

@@ -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(),