pg-api 0.1.0

A high-performance PostgreSQL REST API driver with rate limiting, connection pooling, and observability
use axum::{
    extract::Request,
    http::{HeaderMap, StatusCode},
    middleware::Next,
    response::Response,
};
use uuid::Uuid;

#[allow(unused_imports)]
use crate::models::{Account, AppState};

pub async fn auth_middleware(
    axum::extract::State(state): axum::extract::State<AppState>,
    mut request: Request,
    next: Next,
) -> Result<Response, StatusCode> {
    // Skip auth for health check
    if request.uri().path() == "/health" {
        return Ok(next.run(request).await);
    }
    
    let api_key = extract_api_key(request.headers()).map_err(|e| {
        eprintln!("[AUTH] Failed to extract API key: {}", e);
        StatusCode::UNAUTHORIZED
    })?;
    
    eprintln!("[AUTH] Checking API key: {}...", &api_key[..20.min(api_key.len())]);
    
    // Validate API key
    let account = {
        let mut accounts = state.accounts.write().await;
        eprintln!("[AUTH] Total accounts loaded: {}", accounts.len());
        if let Some(account) = accounts.get_mut(&api_key) {
            account.last_used = chrono::Utc::now();
            Some(account.clone())
        } else {
            eprintln!("[AUTH] API key not found in {} accounts", accounts.len());
            None
        }
    }; // Release the lock here
    
    if let Some(account) = account {
        // Add account info to request extensions
        request.extensions_mut().insert(account);
        Ok(next.run(request).await)
    } else {
        Err(StatusCode::UNAUTHORIZED)
    }
}

pub async fn request_id_middleware(
    mut request: Request,
    next: Next,
) -> Response {
    let request_id = Uuid::new_v4().to_string();
    request.headers_mut().insert(
        "x-request-id",
        request_id.parse().unwrap(),
    );
    
    next.run(request).await
}

pub fn extract_api_key(headers: &HeaderMap) -> Result<String, String> {
    headers
        .get("x-api-key")
        .or_else(|| headers.get("authorization"))
        .and_then(|h| h.to_str().ok())
        .map(|h| {
            if h.starts_with("Bearer ") {
                h.strip_prefix("Bearer ").unwrap().to_string()
            } else {
                h.to_string()
            }
        })
        .ok_or_else(|| "Missing API key".to_string())
}