pub mod queries;
pub mod tracker;
pub use crate::mcp_policy::types::{PolicyRateLimit, RateLimitDimension};
pub use queries::{
check_policy_rate_limits, check_policy_rate_limits_for, get_all_rate_limits,
get_all_rate_limits_for, get_daily_usage, get_daily_usage_for, init_policy_rate_limits,
init_policy_rate_limits_for, record_policy_rate_limits, record_policy_rate_limits_for,
ActionUsage, DailyUsage,
};
pub use tracker::{
check_and_increment_rate_limit, check_and_increment_rate_limit_for, check_rate_limit,
check_rate_limit_for, increment_rate_limit, increment_rate_limit_for,
};
use super::DbPool;
use crate::config::{IntervalsConfig, LimitsConfig};
use crate::error::StorageError;
use super::accounts::DEFAULT_ACCOUNT_ID;
#[derive(Debug, Clone, sqlx::FromRow, serde::Serialize)]
pub struct RateLimit {
pub action_type: String,
pub request_count: i64,
pub period_start: String,
pub max_requests: i64,
pub period_seconds: i64,
}
pub async fn init_rate_limits_for(
pool: &DbPool,
account_id: &str,
config: &LimitsConfig,
intervals: &IntervalsConfig,
) -> Result<(), StorageError> {
let _ = intervals;
let defaults: Vec<(&str, i64, i64)> = vec![
("reply", i64::from(config.max_replies_per_day), 86400),
("tweet", i64::from(config.max_tweets_per_day), 86400),
("thread", i64::from(config.max_threads_per_week), 604800),
("search", 300, 900),
("mention_check", 180, 900),
];
for (action_type, max_requests, period_seconds) in defaults {
sqlx::query(
"INSERT OR IGNORE INTO rate_limits \
(account_id, action_type, request_count, period_start, max_requests, period_seconds) \
VALUES (?, ?, 0, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), ?, ?)",
)
.bind(account_id)
.bind(action_type)
.bind(max_requests)
.bind(period_seconds)
.execute(pool)
.await
.map_err(|e| StorageError::Query { source: e })?;
}
Ok(())
}
pub async fn init_rate_limits(
pool: &DbPool,
config: &LimitsConfig,
intervals: &IntervalsConfig,
) -> Result<(), StorageError> {
init_rate_limits_for(pool, DEFAULT_ACCOUNT_ID, config, intervals).await
}
pub async fn init_mcp_rate_limit_for(
pool: &DbPool,
account_id: &str,
max_per_hour: u32,
) -> Result<(), StorageError> {
sqlx::query(
"INSERT OR IGNORE INTO rate_limits \
(account_id, action_type, request_count, period_start, max_requests, period_seconds) \
VALUES (?, 'mcp_mutation', 0, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), ?, 3600)",
)
.bind(account_id)
.bind(i64::from(max_per_hour))
.execute(pool)
.await
.map_err(|e| StorageError::Query { source: e })?;
Ok(())
}
pub async fn init_mcp_rate_limit(pool: &DbPool, max_per_hour: u32) -> Result<(), StorageError> {
init_mcp_rate_limit_for(pool, DEFAULT_ACCOUNT_ID, max_per_hour).await
}
#[cfg(test)]
mod tests;