first
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use redis::Client as RedisClient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{config::AppConfig, error::AppError};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub config: AppConfig,
|
||||
pub started_at: Instant,
|
||||
pub database_pool: Option<PgPool>,
|
||||
pub redis_client: Option<RedisClient>,
|
||||
current_user_id: Uuid,
|
||||
session: Arc<RwLock<Option<SessionSnapshot>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct SessionStartRequest {
|
||||
pub locale_code: Option<String>,
|
||||
pub device_platform: Option<String>,
|
||||
pub device_model: Option<String>,
|
||||
pub os_version: Option<String>,
|
||||
pub app_version: Option<String>,
|
||||
pub experiment_variant: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SessionSnapshot {
|
||||
pub session_id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub started_at: DateTime<Utc>,
|
||||
pub ended_at: Option<DateTime<Utc>>,
|
||||
pub experiment_variant: String,
|
||||
pub app_version: String,
|
||||
pub device_model: Option<String>,
|
||||
pub os_version: Option<String>,
|
||||
pub locale_code: String,
|
||||
pub device_platform: String,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(
|
||||
config: AppConfig,
|
||||
database_pool: Option<PgPool>,
|
||||
redis_client: Option<RedisClient>,
|
||||
) -> Self {
|
||||
Self {
|
||||
config,
|
||||
started_at: Instant::now(),
|
||||
database_pool,
|
||||
redis_client,
|
||||
current_user_id: Uuid::new_v4(),
|
||||
session: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_session(&self, request: SessionStartRequest) -> SessionSnapshot {
|
||||
let session = SessionSnapshot {
|
||||
session_id: Uuid::new_v4(),
|
||||
user_id: self.current_user_id,
|
||||
started_at: Utc::now(),
|
||||
ended_at: None,
|
||||
experiment_variant: request
|
||||
.experiment_variant
|
||||
.unwrap_or_else(|| self.config.default_experiment_variant.clone()),
|
||||
app_version: request
|
||||
.app_version
|
||||
.unwrap_or_else(|| self.config.app_version.clone()),
|
||||
device_model: request.device_model,
|
||||
os_version: request.os_version,
|
||||
locale_code: request
|
||||
.locale_code
|
||||
.unwrap_or_else(|| self.config.default_locale.clone()),
|
||||
device_platform: request
|
||||
.device_platform
|
||||
.unwrap_or_else(|| self.config.default_device_platform.clone()),
|
||||
};
|
||||
|
||||
*self.session.write().await = Some(session.clone());
|
||||
session
|
||||
}
|
||||
|
||||
pub async fn current_session(&self) -> Option<SessionSnapshot> {
|
||||
self.session.read().await.clone()
|
||||
}
|
||||
|
||||
pub async fn end_session(&self) -> Result<SessionSnapshot, AppError> {
|
||||
let mut guard = self.session.write().await;
|
||||
let mut session = guard
|
||||
.clone()
|
||||
.ok_or_else(|| AppError::not_found("No active session"))?;
|
||||
session.ended_at = Some(Utc::now());
|
||||
*guard = Some(session.clone());
|
||||
Ok(session)
|
||||
}
|
||||
|
||||
pub fn database_ready(&self) -> bool {
|
||||
self.database_pool.is_some()
|
||||
}
|
||||
|
||||
pub fn redis_ready(&self) -> bool {
|
||||
self.redis_client.is_some()
|
||||
}
|
||||
|
||||
pub fn uptime_ms(&self) -> u128 {
|
||||
self.started_at.elapsed().as_millis()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user