use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct GatewayGuardRequest<'a> {
pub user_id: &'a str,
pub model: &'a str,
pub route_id: Option<&'a str>,
pub provider: &'a str,
pub streaming: bool,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum GatewayDenyKind {
#[default]
Quota,
Forbidden,
}
#[derive(Debug, Clone)]
pub struct GatewayDenyReason {
pub message: String,
pub retry_after_seconds: i32,
pub kind: GatewayDenyKind,
}
impl GatewayDenyReason {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
retry_after_seconds: 0,
kind: GatewayDenyKind::Quota,
}
}
pub fn forbidden(message: impl Into<String>) -> Self {
Self {
message: message.into(),
retry_after_seconds: 0,
kind: GatewayDenyKind::Forbidden,
}
}
}
#[async_trait::async_trait]
pub trait GatewayRequestGuard: Send + Sync {
async fn check(
&self,
pool: &sqlx::PgPool,
request: &GatewayGuardRequest<'_>,
) -> Result<(), GatewayDenyReason>;
}
#[derive(Debug, Clone, Copy)]
pub struct GatewayRequestGuardRegistration {
pub factory: fn() -> Arc<dyn GatewayRequestGuard>,
}
inventory::collect!(GatewayRequestGuardRegistration);
#[macro_export]
macro_rules! register_gateway_guard {
($guard_type:ty) => {
::inventory::submit! {
$crate::GatewayRequestGuardRegistration {
factory: || ::std::sync::Arc::new(<$guard_type>::default())
as ::std::sync::Arc<dyn $crate::GatewayRequestGuard>,
}
}
};
($guard_expr:expr) => {
::inventory::submit! {
$crate::GatewayRequestGuardRegistration {
factory: || ::std::sync::Arc::new($guard_expr)
as ::std::sync::Arc<dyn $crate::GatewayRequestGuard>,
}
}
};
}
pub async fn run_gateway_guards(
pool: &sqlx::PgPool,
request: &GatewayGuardRequest<'_>,
) -> Result<(), GatewayDenyReason> {
for registration in inventory::iter::<GatewayRequestGuardRegistration> {
let guard = (registration.factory)();
guard.check(pool, request).await?;
}
Ok(())
}