Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ regex = "1.11.1"
[features]
default = []
docs = []
# WARNING: unsafe-debug enables verbose error/debug output that may expose sensitive data
# NEVER use this feature in production environments
unsafe-debug = []

[dev-dependencies]
tempfile = "3.2"
Expand Down
39 changes: 30 additions & 9 deletions crates/lib/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{account::Account, pubkey::Pubkey};
use tokio::sync::OnceCell;

use crate::error::KoraError;
use crate::{error::KoraError, sanitize_error};

#[cfg(not(test))]
use crate::state::get_config;
Expand Down Expand Up @@ -38,20 +38,29 @@ impl CacheUtil {

let cfg = deadpool_redis::Config::from_url(redis_url);
let pool = cfg.create_pool(Some(Runtime::Tokio1)).map_err(|e| {
KoraError::InternalServerError(format!("Failed to create cache pool: {e}"))
KoraError::InternalServerError(format!(
"Failed to create cache pool: {}",
sanitize_error!(e)
))
})?;

// Test connection
let mut conn = pool.get().await.map_err(|e| {
KoraError::InternalServerError(format!("Failed to connect to cache: {e}"))
KoraError::InternalServerError(format!(
"Failed to connect to cache: {}",
sanitize_error!(e)
))
})?;

// Simple connection test - try to get a non-existent key
let _: Option<String> = conn.get("__connection_test__").await.map_err(|e| {
KoraError::InternalServerError(format!("Cache connection test failed: {e}"))
KoraError::InternalServerError(format!(
"Cache connection test failed: {}",
sanitize_error!(e)
))
})?;

log::info!("Cache initialized successfully with Redis at {redis_url}");
log::info!("Cache initialized successfully");

Some(pool)
} else {
Expand All @@ -68,7 +77,10 @@ impl CacheUtil {

async fn get_connection(pool: &Pool) -> Result<deadpool_redis::Connection, KoraError> {
pool.get().await.map_err(|e| {
KoraError::InternalServerError(format!("Failed to get cache connection: {e}"))
KoraError::InternalServerError(format!(
"Failed to get cache connection: {}",
sanitize_error!(e)
))
})
}

Expand Down Expand Up @@ -100,7 +112,10 @@ impl CacheUtil {
let mut conn = Self::get_connection(pool).await?;

let cached_data: Option<String> = conn.get(key).await.map_err(|e| {
KoraError::InternalServerError(format!("Failed to get from cache: {e}"))
KoraError::InternalServerError(format!(
"Failed to get from cache: {}",
sanitize_error!(e)
))
})?;

match cached_data {
Expand Down Expand Up @@ -147,11 +162,17 @@ impl CacheUtil {
let mut conn = Self::get_connection(pool).await?;

let serialized = serde_json::to_string(data).map_err(|e| {
KoraError::InternalServerError(format!("Failed to serialize cache data: {e}"))
KoraError::InternalServerError(format!(
"Failed to serialize cache data: {}",
sanitize_error!(e)
))
})?;

conn.set_ex::<_, _, ()>(key, serialized, ttl_seconds).await.map_err(|e| {
KoraError::InternalServerError(format!("Failed to set cache data: {e}"))
KoraError::InternalServerError(format!(
"Failed to set cache data: {}",
sanitize_error!(e)
))
})?;

Ok(())
Expand Down
34 changes: 21 additions & 13 deletions crates/lib/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ use crate::{
error::KoraError,
fee::price::{PriceConfig, PriceModel},
oracle::PriceSource,
sanitize_error,
};

#[derive(Debug, Clone, Deserialize)]
#[derive(Clone, Deserialize)]
pub struct Config {
pub validation: ValidationConfig,
pub kora: KoraConfig,
#[serde(default)]
pub metrics: MetricsConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[derive(Clone, Serialize, Deserialize, ToSchema)]
pub struct MetricsConfig {
pub enabled: bool,
pub endpoint: String,
Expand All @@ -48,7 +49,7 @@ impl Default for MetricsConfig {
}
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[derive(Clone, Serialize, Deserialize, ToSchema)]
pub struct FeePayerBalanceMetricsConfig {
pub enabled: bool,
pub expiry_seconds: u64,
Expand Down Expand Up @@ -307,7 +308,7 @@ fn default_max_request_body_size() -> usize {
DEFAULT_MAX_REQUEST_BODY_SIZE
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[derive(Clone, Serialize, Deserialize, ToSchema)]
pub struct CacheConfig {
/// Redis URL for caching (e.g., "redis://localhost:6379")
pub url: Option<String>,
Expand All @@ -330,7 +331,7 @@ impl Default for CacheConfig {
}
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[derive(Clone, Serialize, Deserialize, ToSchema)]
pub struct KoraConfig {
pub rate_limit: u64,
#[serde(default = "default_max_request_body_size")]
Expand Down Expand Up @@ -361,7 +362,7 @@ impl Default for KoraConfig {
}
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[derive(Clone, Serialize, Deserialize, ToSchema)]
pub struct UsageLimitConfig {
/// Enable per-wallet usage limiting
pub enabled: bool,
Expand All @@ -384,7 +385,7 @@ impl Default for UsageLimitConfig {
}
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[derive(Clone, Serialize, Deserialize, ToSchema)]
pub struct AuthConfig {
pub api_key: Option<String>,
pub hmac_secret: Option<String>,
Expand All @@ -401,16 +402,25 @@ impl Default for AuthConfig {
impl Config {
pub fn load_config<P: AsRef<Path>>(path: P) -> Result<Config, KoraError> {
let contents = fs::read_to_string(path).map_err(|e| {
KoraError::InternalServerError(format!("Failed to read config file: {e}"))
KoraError::InternalServerError(format!(
"Failed to read config file: {}",
sanitize_error!(e)
))
})?;

let mut config: Config = toml::from_str(&contents).map_err(|e| {
KoraError::InternalServerError(format!("Failed to parse config file: {e}"))
KoraError::InternalServerError(format!(
"Failed to parse config file: {}",
sanitize_error!(e)
))
})?;

// Initialize Token2022Config to parse and cache extensions
config.validation.token_2022.initialize().map_err(|e| {
KoraError::InternalServerError(format!("Failed to initialize Token2022 config: {e}"))
KoraError::InternalServerError(format!(
"Failed to initialize Token2022 config: {}",
sanitize_error!(e)
))
})?;

Ok(config)
Expand All @@ -422,9 +432,7 @@ impl KoraConfig {
pub fn get_payment_address(&self, signer_pubkey: &Pubkey) -> Result<Pubkey, KoraError> {
if let Some(payment_address_str) = &self.payment_address {
let payment_address = Pubkey::from_str(payment_address_str).map_err(|_| {
KoraError::InternalServerError(format!(
"Invalid payment_address: {payment_address_str}"
))
KoraError::InternalServerError("Invalid payment_address format".to_string())
})?;
Ok(payment_address)
} else {
Expand Down
Loading