litellm-rs 0.4.16

A high-performance AI Gateway written in Rust, providing OpenAI-compatible APIs with intelligent routing, load balancing, and enterprise features
Documentation
//! Database-backed API key repository implementation.

use super::db_mapping::{from_domain_api_key, to_domain_api_key};
use super::db_update::apply_update_config;
use super::repository::KeyRepository;
use super::types::{KeyStatus, ManagedApiKey, UpdateKeyConfig};
use crate::storage::StorageLayer;
use crate::utils::error::gateway_error::{GatewayError, Result};
use async_trait::async_trait;
use std::sync::Arc;
use uuid::Uuid;

#[derive(Debug, Clone)]
pub struct DatabaseKeyRepository {
    storage: Arc<StorageLayer>,
}

impl DatabaseKeyRepository {
    pub fn new(storage: Arc<StorageLayer>) -> Self {
        Self { storage }
    }
}

#[async_trait]
impl KeyRepository for DatabaseKeyRepository {
    async fn create(&self, key: ManagedApiKey) -> Result<ManagedApiKey> {
        let domain_key = to_domain_api_key(&key)?;
        let stored = self.storage.db().create_api_key(&domain_key).await?;
        Ok(from_domain_api_key(&stored))
    }

    async fn find_by_id(&self, id: Uuid) -> Result<Option<ManagedApiKey>> {
        let key = self.storage.db().find_api_key_by_id(id).await?;
        Ok(key.map(|k| from_domain_api_key(&k)))
    }

    async fn find_by_hash(&self, key_hash: &str) -> Result<Option<ManagedApiKey>> {
        let key = self.storage.db().find_api_key_by_hash(key_hash).await?;
        Ok(key.map(|k| from_domain_api_key(&k)))
    }

    async fn update(&self, id: Uuid, config: UpdateKeyConfig) -> Result<ManagedApiKey> {
        let mut domain_key = self
            .storage
            .db()
            .find_api_key_by_id(id)
            .await?
            .ok_or_else(|| GatewayError::not_found("API key not found"))?;

        apply_update_config(&mut domain_key, config)?;
        let updated = self.storage.db().update_api_key(&domain_key).await?;
        Ok(from_domain_api_key(&updated))
    }

    async fn update_status(&self, id: Uuid, status: KeyStatus) -> Result<()> {
        match status {
            KeyStatus::Active => self.storage.db().set_api_key_active(id, true).await,
            KeyStatus::Revoked => self.storage.db().set_api_key_active(id, false).await,
            KeyStatus::Expired => Err(GatewayError::validation(
                "Cannot set key status to expired manually",
            )),
        }
    }

    async fn update_last_used(&self, id: Uuid) -> Result<()> {
        self.storage.db().update_api_key_last_used(id).await
    }

    async fn update_usage(&self, id: Uuid, tokens: u64, cost: f64) -> Result<()> {
        self.storage
            .db()
            .update_api_key_usage(id, 1, tokens, cost)
            .await
    }

    async fn list_by_user(&self, user_id: Uuid) -> Result<Vec<ManagedApiKey>> {
        let keys = self.storage.db().list_api_keys_by_user(user_id).await?;
        Ok(keys.into_iter().map(|k| from_domain_api_key(&k)).collect())
    }

    async fn list_by_team(&self, team_id: Uuid) -> Result<Vec<ManagedApiKey>> {
        let keys = self.storage.db().list_api_keys_by_team(team_id).await?;
        Ok(keys.into_iter().map(|k| from_domain_api_key(&k)).collect())
    }

    async fn list_all(
        &self,
        status: Option<KeyStatus>,
        limit: Option<usize>,
        offset: Option<usize>,
    ) -> Result<Vec<ManagedApiKey>> {
        let keys = self
            .storage
            .db()
            .list_api_keys(status, limit, offset)
            .await?;
        Ok(keys.into_iter().map(|k| from_domain_api_key(&k)).collect())
    }

    async fn delete(&self, id: Uuid) -> Result<()> {
        self.storage.db().delete_api_key(id).await
    }

    async fn delete_expired(&self) -> Result<u64> {
        self.storage.db().delete_expired_api_keys().await
    }

    async fn count(&self, status: Option<KeyStatus>) -> Result<u64> {
        self.storage.db().count_api_keys(status).await
    }
}