You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

293 lines
7.4 KiB

use crate::api::CurrentUser;
use crate::rds_conn::RdsConn;
use chrono::{offset::Local, DateTime};
use redis::{AsyncCommands, RedisResult};
use rocket::serde::json::serde_json;
use rocket::serde::{Deserialize, Serialize};
macro_rules! init {
() => {
pub fn init(rconn: &RdsConn) -> Self {
Self {
rconn: rconn.clone(),
}
}
};
($ktype:ty, $formatter:expr) => {
pub fn init(k: $ktype, rconn: &RdsConn) -> Self {
Self {
key: format!($formatter, k),
rconn: rconn.clone(),
}
}
};
($k1type:ty, $k2type:ty, $formatter:expr) => {
pub fn init(k1: $k1type, k2: $k2type, rconn: &RdsConn) -> Self {
Self {
key: format!($formatter, k1, k2),
rconn: rconn.clone(),
}
}
};
}
macro_rules! has {
($vtype:ty) => {
pub async fn has(&mut self, v: $vtype) -> RedisResult<bool> {
self.rconn.sismember(&self.key, v).await
}
};
}
macro_rules! add {
($vtype:ty) => {
pub async fn add(&mut self, v: $vtype) -> RedisResult<usize> {
self.rconn.sadd(&self.key, v).await
}
};
}
const KEY_SYSTEMLOG: &str = "hole_v2:systemlog_list";
const KEY_BANNED_USERS: &str = "hole_v2:banned_user_hash_list";
const KEY_BLOCKED_COUNTER: &str = "hole_v2:blocked_counter";
const KEY_CUSTOM_TITLE: &str = "hole_v2:title";
const KEY_AUTO_BLOCK_RANK: &str = "hole_v2:auto_block_rank"; // rank * 5: 自动过滤的拉黑数阈值
const SYSTEMLOG_MAX_LEN: isize = 1000;
pub struct Attention {
key: String,
rconn: RdsConn,
}
impl Attention {
init!(&str, "hole_v2:attention:{}");
add!(i32);
has!(i32);
pub async fn remove(&mut self, pid: i32) -> RedisResult<()> {
self.rconn.srem(&self.key, pid).await
}
pub async fn all(&mut self) -> RedisResult<Vec<i32>> {
self.rconn.smembers(&self.key).await
}
pub async fn clear_all(rconn: &RdsConn) {
let mut rconn = rconn.clone();
let mut keys = rconn
.scan_match::<&str, String>("hole_v2:attention:*")
.await
.unwrap();
let mut ks_for_del = Vec::new();
while let Some(key) = keys.next_item().await {
ks_for_del.push(key);
}
if ks_for_del.is_empty() {
return;
}
rconn
.del(ks_for_del)
.await
.unwrap_or_else(|e| warn!("clear all post cache fail, {}", e));
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(crate = "rocket::serde")]
pub enum LogType {
AdminDelete,
Report,
Ban,
}
/*
impl LogType {
pub fn contains_ugc(&self) -> bool {
match self {
Self::Report => true,
_ => false,
}
}
}
*/
#[derive(Serialize, Deserialize, Debug)]
#[serde(crate = "rocket::serde")]
pub struct Systemlog {
pub user_hash: String,
pub action_type: LogType,
pub target: String,
pub detail: String,
pub time: DateTime<Local>,
}
impl Systemlog {
pub async fn create(&self, rconn: &RdsConn) -> RedisResult<()> {
let mut rconn = rconn.clone();
if rconn.llen::<&str, isize>(KEY_SYSTEMLOG).await? > SYSTEMLOG_MAX_LEN {
rconn.ltrim(KEY_SYSTEMLOG, 0, SYSTEMLOG_MAX_LEN - 1).await?;
}
rconn
.lpush(KEY_SYSTEMLOG, serde_json::to_string(&self).unwrap())
.await
}
pub async fn get_list(rconn: &RdsConn, limit: isize) -> RedisResult<Vec<Self>> {
let rds_result = rconn
.clone()
.lrange::<&str, Vec<String>>(KEY_SYSTEMLOG, 0, limit)
.await?;
Ok(rds_result
.iter()
.map(|s| serde_json::from_str(s).unwrap())
.collect())
}
}
pub struct BannedUsers;
impl BannedUsers {
pub async fn add(rconn: &RdsConn, namehash: &str) -> RedisResult<()> {
rconn
.clone()
.sadd::<&str, &str, ()>(KEY_BANNED_USERS, namehash)
.await
}
pub async fn has(rconn: &RdsConn, namehash: &str) -> RedisResult<bool> {
rconn.clone().sismember(KEY_BANNED_USERS, namehash).await
}
pub async fn clear(rconn: &RdsConn) -> RedisResult<()> {
rconn.clone().del(KEY_BANNED_USERS).await
}
}
pub struct BlockedUsers {
pub key: String,
rconn: RdsConn,
}
impl BlockedUsers {
init!(i32, "hole_v2:blocked_users:{}");
add!(&str);
has!(&str);
pub async fn check_if_block(
rconn: &RdsConn,
user: &CurrentUser,
hash: &str,
) -> RedisResult<bool> {
Ok(match user.id {
Some(id) => BlockedUsers::init(id, rconn).has(hash).await?,
None => false,
} || BlockCounter::get_count(rconn, hash).await?.unwrap_or(0)
>= i32::from(user.auto_block_rank) * 5)
}
}
pub struct BlockCounter;
impl BlockCounter {
pub async fn count_incr(rconn: &RdsConn, namehash: &str) -> RedisResult<usize> {
rconn.clone().hincr(KEY_BLOCKED_COUNTER, namehash, 1).await
}
pub async fn get_count(rconn: &RdsConn, namehash: &str) -> RedisResult<Option<i32>> {
rconn.clone().hget(KEY_BLOCKED_COUNTER, namehash).await
}
}
pub struct CustomTitle;
impl CustomTitle {
// return false if title exits
pub async fn set(rconn: &RdsConn, namehash: &str, title: &str) -> RedisResult<bool> {
let mut rconn = rconn.clone();
if rconn.hexists(KEY_CUSTOM_TITLE, title).await? {
Ok(false)
} else {
rconn.hset(KEY_CUSTOM_TITLE, namehash, title).await?;
rconn.hset(KEY_CUSTOM_TITLE, title, namehash).await?;
Ok(true)
}
}
pub async fn get(rconn: &RdsConn, namehash: &str) -> RedisResult<Option<String>> {
rconn.clone().hget(KEY_CUSTOM_TITLE, namehash).await
}
pub async fn clear(rconn: &RdsConn) -> RedisResult<()> {
rconn.clone().del(KEY_CUSTOM_TITLE).await
}
}
pub struct AutoBlockRank;
impl AutoBlockRank {
pub async fn set(rconn: &RdsConn, namehash: &str, rank: u8) -> RedisResult<usize> {
rconn
.clone()
.hset(KEY_AUTO_BLOCK_RANK, namehash, rank)
.await
}
pub async fn get(rconn: &RdsConn, namehash: &str) -> RedisResult<u8> {
let rank: Option<u8> = rconn.clone().hget(KEY_AUTO_BLOCK_RANK, namehash).await?;
Ok(rank.unwrap_or(4))
}
pub async fn clear(rconn: &RdsConn) -> RedisResult<()> {
rconn.clone().del(KEY_AUTO_BLOCK_RANK).await
}
}
pub struct PollOption {
key: String,
rconn: RdsConn,
}
impl PollOption {
init!(i32, "hole_thu:poll_opts:{}");
pub async fn set_list(&mut self, v: &Vec<String>) -> RedisResult<()> {
self.rconn.del(&self.key).await?;
self.rconn.rpush(&self.key, v).await
}
pub async fn get_list(&mut self) -> RedisResult<Vec<String>> {
self.rconn.lrange(&self.key, 0, -1).await
}
}
pub struct PollVote {
key: String,
rconn: RdsConn,
}
impl PollVote {
init!(i32, usize, "hole_thu:poll_votes:{}:{}");
add!(&str);
has!(&str);
pub async fn count(&mut self) -> RedisResult<usize> {
self.rconn.scard(&self.key).await
}
}
pub async fn clear_outdate_redis_data(rconn: &RdsConn) {
BannedUsers::clear(rconn).await.unwrap();
CustomTitle::clear(rconn).await.unwrap();
AutoBlockRank::clear(rconn).await.unwrap();
Attention::clear_all(rconn).await;
}
pub(crate) use init;