diff --git a/Cargo.toml b/Cargo.toml index 1f92b83..38d322b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ rocket = { version = "=0.5.0-rc.2", features = ["json"] } rocket_sync_db_pools = { version = "=0.1.0-rc.2", features = ["diesel_postgres_pool"] } diesel = { version = "1.4.8", features = ["postgres", "chrono"] } diesel_migrations = "1.4.0" -redis = { version="0.21.5", features = ["aio", "tokio-comp"] } +redis = { version="0.23.0", features = ["aio", "tokio-comp"] } chrono = { version="0.4.19", features = ["serde"] } rand = "0.8.5" dotenv = "0.15.0" @@ -25,5 +25,6 @@ env_logger = "0.9.0" web-push = "0.9.2" url = "2.2.2" futures = "0.3.24" +futures-util = "0.3.24" reqwest = { version = "0.11.10", features = ["json"], optional = true } diff --git a/src/cache.rs b/src/cache.rs index 4270dc6..501e4ee 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,11 +1,12 @@ use crate::api::CurrentUser; use crate::models::{Comment, Post, User}; use crate::rds_conn::RdsConn; -use crate::rds_models::{init, BlockedUsers}; +use crate::rds_models::{clear_all, init, BlockedUsers}; use rand::Rng; use redis::{AsyncCommands, RedisError, RedisResult}; use rocket::serde::json::serde_json; // can use rocket::serde::json::to_string in master version +use futures_util::stream::StreamExt; use rocket::futures::future; use std::collections::HashMap; @@ -28,6 +29,8 @@ pub struct PostCache { impl PostCache { init!(); + clear_all!("hole_v2:cache::post:*:v2"); + pub async fn sets(&mut self, ps: &[&Post]) { if ps.is_empty() { return; @@ -36,7 +39,7 @@ impl PostCache { .iter() .map(|p| (post_cache_key!(p.id), serde_json::to_string(p).unwrap())) .collect(); - self.rconn.set_multiple(&kvs).await.unwrap_or_else(|e| { + self.rconn.mset(&kvs).await.unwrap_or_else(|e| { warn!("set post cache failed: {}", e); dbg!(&kvs); }); @@ -97,27 +100,6 @@ impl PostCache { } } } - - pub async fn clear_all(&mut self) { - let mut keys = self - .rconn - .scan_match::(post_cache_key!("*")) - .await - .unwrap(); //.collect::>().await; - // colllect() does not work - // also see: https://github.com/mitsuhiko/redis-rs/issues/583 - 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; - } - self.rconn - .del(ks_for_del) - .await - .unwrap_or_else(|e| warn!("clear all post cache fail, {}", e)); - } } pub struct PostCommentCache { @@ -295,6 +277,8 @@ pub struct UserCache { impl UserCache { init!(&str, "hole_v2:cache:user:{}"); + clear_all!("hole_v2:cache:user:*"); + pub async fn set(&mut self, u: &User) { self.rconn .set_ex( diff --git a/src/login.rs b/src/login.rs index 5d600ff..58901cd 100644 --- a/src/login.rs +++ b/src/login.rs @@ -2,9 +2,11 @@ use crate::db_conn::Db; use crate::models::User; +use crate::random_hasher::RandomHasher; use rocket::request::{FromRequest, Outcome, Request}; use rocket::response::Redirect; use rocket::serde::Deserialize; +use rocket::State; use std::env; use url::Url; @@ -74,6 +76,7 @@ pub async fn cs_auth( redirect_url: String, jump_to_url: String, db: Db, + rh: &State, ) -> Result { if !env::var("FRONTEND_WHITELIST") .unwrap_or_default() @@ -130,9 +133,13 @@ pub async fn cs_auth( //dbg!(&account); - let tk = User::find_or_create_token(&db, &format!("cs_{}", &account.id), false) - .await - .unwrap(); + let tk = User::find_or_create_token( + &db, + &rh.hash_with_salt(&format!("cs_{}", &account.id)), + false, + ) + .await + .unwrap(); Ok(Redirect::to(format!("{}?token={}", &jump_to_url, &tk))) } @@ -177,7 +184,12 @@ struct GithubEmail { } #[get("/gh/auth?&")] -pub async fn gh_auth(code: String, jump_to_url: String, db: Db) -> Result { +pub async fn gh_auth( + code: String, + jump_to_url: String, + db: Db, + rh: &State, +) -> Result { if !env::var("FRONTEND_WHITELIST") .unwrap_or_default() .split(',') @@ -230,9 +242,13 @@ pub async fn gh_auth(code: String, jump_to_url: String, db: Db) -> Result { + ($ktype:ty, $formatter:literal) => { pub fn init(k: $ktype, rconn: &RdsConn) -> Self { Self { key: format!($formatter, k), @@ -22,7 +23,7 @@ macro_rules! init { } } }; - ($k1type:ty, $k2type:ty, $formatter:expr) => { + ($k1type:ty, $k2type:ty, $formatter:literal) => { pub fn init(k1: $k1type, k2: $k2type, rconn: &RdsConn) -> Self { Self { key: format!($formatter, k1, k2), @@ -56,6 +57,24 @@ macro_rules! rem { }; } +macro_rules! clear_all { + ($pattern:literal) => { + pub async fn clear_all(rconn: &mut RdsConn) { + let keys: Vec = rconn + .scan_match::<&str, String>($pattern) + .await + .unwrap() + .collect::>() + .await; + + rconn + .del(keys) + .await + .unwrap_or_else(|e| warn!("clear all fail, pattern: {} , {}", $pattern, e)); + } + }; +} + 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"; @@ -85,6 +104,8 @@ impl Attention { has!(i32); + clear_all!("hole_v2:attention:*"); + pub async fn remove(&mut self, pid: i32) -> RedisResult<()> { self.rconn.srem(&self.key, pid).await } @@ -92,26 +113,6 @@ impl Attention { pub async fn all(&mut self) -> RedisResult> { 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)); - } } pub struct Reaction { @@ -208,8 +209,8 @@ impl BannedUsers { rconn.clone().sismember(KEY_BANNED_USERS, namehash).await } - pub async fn clear(rconn: &RdsConn) -> RedisResult<()> { - rconn.clone().del(KEY_BANNED_USERS).await + pub async fn clear(rconn: &mut RdsConn) -> RedisResult<()> { + rconn.del(KEY_BANNED_USERS).await } } @@ -225,6 +226,8 @@ impl BlockedUsers { has!(&str); + clear_all!("hole_v2:blocked_users:*"); + pub async fn check_if_block( rconn: &RdsConn, user: &CurrentUser, @@ -308,8 +311,8 @@ impl CustomTitle { }) } - pub async fn clear(rconn: &RdsConn) -> RedisResult<()> { - rconn.clone().del(KEY_CUSTOM_TITLE).await + pub async fn clear(rconn: &mut RdsConn) -> RedisResult<()> { + rconn.del(KEY_CUSTOM_TITLE).await } } @@ -328,8 +331,8 @@ impl AutoBlockRank { Ok(rank.unwrap_or(4)) } - pub async fn clear(rconn: &RdsConn) -> RedisResult<()> { - rconn.clone().del(KEY_AUTO_BLOCK_RANK).await + pub async fn clear(rconn: &mut RdsConn) -> RedisResult<()> { + rconn.del(KEY_AUTO_BLOCK_RANK).await } } @@ -368,11 +371,12 @@ impl PollVote { } } -pub async fn clear_outdate_redis_data(rconn: &RdsConn) { +pub async fn clear_outdate_redis_data(rconn: &mut RdsConn) { BannedUsers::clear(rconn).await.unwrap(); CustomTitle::clear(rconn).await.unwrap(); AutoBlockRank::clear(rconn).await.unwrap(); Attention::clear_all(rconn).await; + BlockedUsers::clear_all(rconn).await; } pub async fn get_announcement(rconn: &RdsConn) -> RedisResult> { @@ -410,4 +414,5 @@ pub async fn clear_title_from_admins(rconn: &RdsConn, title: &str) -> RedisResul Ok(()) } +pub(crate) use clear_all; pub(crate) use init;