feat: add ratelimits to gifbox

This commit is contained in:
Zomatree
2025-09-18 21:46:08 +01:00
parent 06a281ec7e
commit 50a926f0a2
4 changed files with 61 additions and 22 deletions

1
Cargo.lock generated
View File

@@ -6519,6 +6519,7 @@ dependencies = [
"revolt-config", "revolt-config",
"revolt-database", "revolt-database",
"revolt-models", "revolt-models",
"revolt-ratelimits",
"revolt-result", "revolt-result",
"serde", "serde",
"serde_json", "serde_json",

View File

@@ -28,6 +28,9 @@ revolt-coalesced = { version = "0.8.8", path = "../../core/coalesced", features
revolt-database = { version = "0.8.8", path = "../../core/database", features = [ revolt-database = { version = "0.8.8", path = "../../core/database", features = [
"axum-impl", "axum-impl",
] } ] }
revolt-ratelimits = { version = "0.8.8", path = "../../core/ratelimits", features = [
"axum",
] }
# Axum / web server # Axum / web server
axum = { version = "0.7.5" } axum = { version = "0.7.5" }

View File

@@ -1,9 +1,10 @@
use std::net::{Ipv4Addr, SocketAddr}; use std::net::{Ipv4Addr, SocketAddr};
use axum::{extract::FromRef, Router}; use axum::{extract::FromRef, middleware::from_fn_with_state, Router};
use revolt_config::config; use revolt_config::config;
use revolt_database::{Database, DatabaseInfo}; use revolt_database::{Database, DatabaseInfo};
use revolt_ratelimits::axum as ratelimiter;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use utoipa::{ use utoipa::{
openapi::security::{ApiKey, ApiKeyValue, SecurityScheme}, openapi::security::{ApiKey, ApiKeyValue, SecurityScheme},
@@ -13,31 +14,21 @@ use utoipa_scalar::{Scalar, Servable as ScalarServable};
use crate::tenor::Tenor; use crate::tenor::Tenor;
mod ratelimits;
mod routes; mod routes;
mod tenor; mod tenor;
mod types; mod types;
#[derive(Clone)] #[derive(Clone, FromRef)]
struct AppState { struct AppState {
pub database: Database, pub database: Database,
pub tenor: Tenor, pub tenor: Tenor,
pub ratelimit_storage: ratelimiter::RatelimitStorage,
} }
impl FromRef<AppState> for Database { struct SecurityAddon;
fn from_ref(state: &AppState) -> Self {
state.database.clone()
}
}
impl FromRef<AppState> for Tenor { impl Modify for SecurityAddon {
fn from_ref(state: &AppState) -> Self {
state.tenor.clone()
}
}
struct TokenAddon;
impl Modify for TokenAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) { fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
let components = openapi.components.get_or_insert_default(); let components = openapi.components.get_or_insert_default();
@@ -63,7 +54,7 @@ async fn main() -> Result<(), std::io::Error> {
// Configure API schema // Configure API schema
#[derive(OpenApi)] #[derive(OpenApi)]
#[openapi( #[openapi(
modifiers(&TokenAddon), modifiers(&SecurityAddon),
paths( paths(
routes::categories::categories, routes::categories::categories,
routes::root::root, routes::root::root,
@@ -86,18 +77,30 @@ async fn main() -> Result<(), std::io::Error> {
struct ApiDoc; struct ApiDoc;
let config = config().await; let config = config().await;
let state = AppState {
database: DatabaseInfo::Auto let database = DatabaseInfo::Auto
.connect() .connect()
.await .await
.expect("Unable to connect to database"), .expect("Unable to connect to database");
tenor: tenor::Tenor::new(&config.api.security.tenor_key),
let tenor = tenor::Tenor::new(&config.api.security.tenor_key);
let ratelimit_storage = ratelimiter::RatelimitStorage::new(ratelimits::GifboxRatelimits);
let state = AppState {
database,
tenor,
ratelimit_storage,
}; };
// Configure Axum and router // Configure Axum and router
let app = Router::new() let app = Router::new()
.merge(Scalar::with_url("/scalar", ApiDoc::openapi())) .merge(Scalar::with_url("/scalar", ApiDoc::openapi()))
.nest("/", routes::router()) .nest("/", routes::router())
.layer(from_fn_with_state(
state.clone(),
ratelimiter::ratelimit_middleware,
))
.with_state(state); .with_state(state);
// Configure TCP listener and bind // Configure TCP listener and bind

View File

@@ -0,0 +1,32 @@
use axum::http::request::Parts;
use revolt_ratelimits::ratelimiter::RatelimitResolver;
pub struct GifboxRatelimits;
impl RatelimitResolver<Parts> for GifboxRatelimits {
fn resolve_bucket<'a>(&self, parts: &'a Parts) -> (&'a str, Option<&'a str>) {
let path = parts
.uri
.path()
.trim_matches('/')
.split_terminator("/")
.next();
match path {
Some("categories") => ("categories", None),
Some("trending") => ("trending", None),
Some("search") => ("search", None),
_ => ("any", None),
}
}
fn resolve_bucket_limit(&self, bucket: &str) -> u32 {
match bucket {
"categories" => 2,
"trending" => 5,
"search" => 10,
"any" => u32::MAX,
_ => unreachable!("Bucket defined but no limit set"),
}
}
}