|
|
|
@ -169,6 +169,34 @@ pub async fn test_key(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// `POST /api/v1/user/api-keys/export`
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Returns decrypted API keys for settings export backup.
|
|
|
|
|
|
|
|
/// Rate-limited to 3 calls per minute. Uses POST to require CSRF token.
|
|
|
|
|
|
|
|
pub async fn export_keys(
|
|
|
|
|
|
|
|
auth_user: AuthUser,
|
|
|
|
|
|
|
|
State(state): State<AppState>,
|
|
|
|
|
|
|
|
) -> Result<impl IntoResponse, AppError> {
|
|
|
|
|
|
|
|
if !state.auth_rate_limiter.check(&format!("key-export:{}", auth_user.id)) {
|
|
|
|
|
|
|
|
return Err(AppError::RateLimited("Too many export requests".into()));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let stored_keys = db::api_keys::list_for_user(&state.pool, auth_user.id).await?;
|
|
|
|
|
|
|
|
let master_key = encryption::MasterKey::from_hex(&state.config.master_encryption_key)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut exported = Vec::new();
|
|
|
|
|
|
|
|
for key in &stored_keys {
|
|
|
|
|
|
|
|
let decrypted = encryption::decrypt(&master_key, &key.encrypted_key, &key.nonce)?;
|
|
|
|
|
|
|
|
exported.push(serde_json::json!({
|
|
|
|
|
|
|
|
"provider_name": key.provider_name,
|
|
|
|
|
|
|
|
"api_key": decrypted,
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tracing::info!(user_id = %auth_user.id, key_count = exported.len(), "API keys exported");
|
|
|
|
|
|
|
|
Ok(Json(exported))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Get a default model identifier for a provider from the admin config.
|
|
|
|
/// Get a default model identifier for a provider from the admin config.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// Looks up the admin provider configuration for the given provider name
|
|
|
|
/// Looks up the admin provider configuration for the given provider name
|
|
|
|
|