use crate::db::models::User;
use crate::server::state::AppState;
use axum::{extract::State, http::StatusCode, response::IntoResponse, Extension, Json};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
pub struct EncryptRequest {
pub plaintext: String,
}
#[derive(Debug, Serialize)]
pub struct EncryptResponse {
pub encrypted: String,
}
#[derive(Debug, thiserror::Error)]
pub enum EncryptError {
#[error("Rate limit exceeded. Retry after {retry_after} seconds")]
RateLimitExceeded { retry_after: u64 },
#[error("Encryption provider not configured")]
ProviderNotConfigured,
#[error("Encryption failed: {0}")]
EncryptionFailed(String),
}
impl IntoResponse for EncryptError {
fn into_response(self) -> axum::response::Response {
match self {
EncryptError::RateLimitExceeded { retry_after } => {
let mut response =
(StatusCode::TOO_MANY_REQUESTS, self.to_string()).into_response();
let _ = response.headers_mut().try_insert(
axum::http::header::RETRY_AFTER,
axum::http::HeaderValue::from_str(&retry_after.to_string()).unwrap(),
);
response
}
EncryptError::ProviderNotConfigured => {
(StatusCode::SERVICE_UNAVAILABLE, self.to_string()).into_response()
}
EncryptError::EncryptionFailed(_) => {
(StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response()
}
}
}
}
pub async fn encrypt_handler(
State(state): State<AppState>,
Extension(user): Extension<User>,
Json(req): Json<EncryptRequest>,
) -> Result<Json<EncryptResponse>, EncryptError> {
let key = format!("encrypt:{}", user.id);
let count = state.encrypt_rate_limiter.get(&key).await.unwrap_or(0);
if count >= 100 {
return Err(EncryptError::RateLimitExceeded { retry_after: 3600 });
}
let encryption_provider = state
.encryption_provider
.as_ref()
.ok_or(EncryptError::ProviderNotConfigured)?;
let encrypted = encryption_provider
.encrypt(&req.plaintext)
.await
.map_err(|e| EncryptError::EncryptionFailed(e.to_string()))?;
state
.encrypt_rate_limiter
.insert(key.clone(), count + 1)
.await;
Ok(Json(EncryptResponse { encrypted }))
}