Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Loading