Add rate limiting
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
//! # Backend services
|
||||
|
||||
pub mod rate_limiter_service;
|
||||
pub mod users_service;
|
||||
|
68
geneit_backend/src/services/rate_limiter_service.rs
Normal file
68
geneit_backend/src/services/rate_limiter_service.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use crate::connections::redis_connection;
|
||||
use crate::utils::time_utils::time;
|
||||
use std::net::IpAddr;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum RatedAction {
|
||||
CreateAccount,
|
||||
}
|
||||
|
||||
impl RatedAction {
|
||||
fn id(&self) -> &'static str {
|
||||
match self {
|
||||
RatedAction::CreateAccount => "create-account",
|
||||
}
|
||||
}
|
||||
|
||||
fn limit(&self) -> usize {
|
||||
match self {
|
||||
RatedAction::CreateAccount => 5,
|
||||
}
|
||||
}
|
||||
|
||||
fn keep_seconds(&self) -> u64 {
|
||||
match self {
|
||||
RatedAction::CreateAccount => 3600,
|
||||
}
|
||||
}
|
||||
|
||||
fn key(&self, ip: IpAddr) -> String {
|
||||
format!("rate-{}-{}", self.id(), ip)
|
||||
}
|
||||
}
|
||||
|
||||
/// Keep track of the time the action was executed by the user
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||
struct ActionRecord(Vec<u64>);
|
||||
|
||||
impl ActionRecord {
|
||||
pub fn clean(&mut self, action: RatedAction) {
|
||||
self.0.retain(|e| e + action.keep_seconds() > time());
|
||||
}
|
||||
}
|
||||
|
||||
/// Record a new action of the user
|
||||
pub async fn record_action(ip: IpAddr, action: RatedAction) -> anyhow::Result<()> {
|
||||
let key = action.key(ip);
|
||||
|
||||
let mut record = redis_connection::get_value::<ActionRecord>(&key)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
record.clean(action);
|
||||
record.0.push(time());
|
||||
redis_connection::set_value(&key, &record, Duration::from_secs(action.keep_seconds())).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check whether an action should be blocked, due to too much attempts from the user
|
||||
pub async fn should_block_action(ip: IpAddr, action: RatedAction) -> anyhow::Result<bool> {
|
||||
let mut record = redis_connection::get_value::<ActionRecord>(&action.key(ip))
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
|
||||
record.clean(action);
|
||||
|
||||
Ok(record.0.len() >= action.limit())
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
//! # Users service
|
||||
|
||||
use crate::db_connection;
|
||||
use crate::connections::db_connection;
|
||||
use crate::models::{NewUser, User};
|
||||
use crate::schema::users;
|
||||
use crate::utils::time_utils::time;
|
||||
|
Reference in New Issue
Block a user