use redis::{Commands, Client, Connection};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
pub struct TokenUserInfo {
pub created: u64,
pub devId: Option<String>,
pub distId: i32,
pub distributorType: i32,
pub email: String,
pub frozenAll: i32,
pub frozenAsset: i32,
pub frozenCfd: i32,
pub frozenOtc: i32,
pub frozenSpot: i32,
pub invite: Option<String>,
pub ip: u64,
pub isMarketMaker: Option<i32>,
pub mobile: String,
pub parentId: i64,
pub parentInvite: Option<String>,
pub partnerId: i64,
pub salesId: i32,
pub status: i32,
pub userId: i64,
pub username: Option<String>,
pub lastIp: Option<String>,
}
#[derive(Debug, Clone)]
pub struct AuthConfig {
pub redis_url: String,
pub header_key: String,
}
impl Default for AuthConfig {
fn default() -> Self {
Self {
redis_url: "redis://127.0.0.1/".into(),
header_key: "X-Authorization".into(),
}
}
}
#[derive(Debug, Error)]
pub enum AuthError {
#[error("Missing auth header")]
MissingHeader,
#[error("Redis connection error: {0}")]
ConnectionError(String),
#[error("Redis command error: {0}")]
CommandError(String),
#[error("Deserialization error: {0}")]
DeserializationError(String),
}
#[derive(Clone)]
pub struct AuthClient {
pub client: Client,
pub config: AuthConfig,
}
impl AuthClient {
pub fn new(config: AuthConfig) -> Result<Self, AuthError> {
let client = Client::open(config.redis_url.clone())
.map_err(|e| AuthError::ConnectionError(e.to_string()))?;
Ok(Self { client, config })
}
fn get_conn(&self) -> Result<Connection, AuthError> {
self.client.get_connection()
.map_err(|e| AuthError::ConnectionError(e.to_string()))
}
pub fn get_user_data<T: DeserializeOwned>(
&self,
token: &str,
) -> Result<T, AuthError> {
let mut conn = self.get_conn()?;
let redis_key1 = format!("kubiex:session:id:{}", token);
let user_id: Option<String> = conn.get(&redis_key1)
.map_err(|e| AuthError::CommandError(e.to_string()))?;
let data = if let Some(user_id) = user_id {
let redis_key2 = format!("kubiex:session:user_id:{}", user_id);
conn.get(&redis_key2)
.map_err(|e| AuthError::CommandError(e.to_string()))?
} else {
String::new()
};
serde_json::from_str(&data)
.map_err(|e| AuthError::DeserializationError(e.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_full_flow() {
let config = AuthConfig {
redis_url: "redis://locklevel-redis-dev1-india.8wuaih.ng.0001.apse1.cache.amazonaws.com:6379/0".into(),
header_key: "X-Authorization".into(),
};
let client = AuthClient::new(config).unwrap();
let result: Result<TokenUserInfo, _> = client.get_user_data("f4a7afb9e538aa9e7f61646864e20466d5671680266603db6b70141821cf9067");
if let Ok(user_info) = result {
println!("Got user info: {:?}", user_info);
}
}
}