This commit is contained in:
2026-04-09 15:04:59 +02:00
parent 4a85efc270
commit 4cc22447c7
12 changed files with 589 additions and 68 deletions
+60 -63
View File
@@ -1,13 +1,19 @@
use std::{sync::Arc, time::Instant};
use std::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};
pub use crate::events::{EventManifestSnapshot, EventSnapshot};
pub use crate::sessions::{SessionSnapshot, SessionStartRequest};
use crate::{
config::AppConfig,
error::AppError,
events::{EventsStore},
sessions::{SessionDefaults, SessionsStore},
users::{UserCreateRequest, UsersStore},
};
#[derive(Clone)]
pub struct AppState {
@@ -15,32 +21,9 @@ pub struct AppState {
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,
events: EventsStore,
users: UsersStore,
sessions: SessionsStore,
}
impl AppState {
@@ -54,49 +37,63 @@ impl AppState {
started_at: Instant::now(),
database_pool,
redis_client,
current_user_id: Uuid::new_v4(),
session: Arc::new(RwLock::new(None)),
events: EventsStore::new(),
users: UsersStore::new(),
sessions: SessionsStore::new(),
}
}
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()),
};
let user = self
.users
.upsert(UserCreateRequest {
external_ref: request.external_ref.clone(),
preferred_language: request
.locale_code
.clone()
.unwrap_or_else(|| self.config.default_locale.clone()),
device_platform: request
.device_platform
.clone()
.unwrap_or_else(|| self.config.default_device_platform.clone()),
})
.await;
*self.session.write().await = Some(session.clone());
session
self.sessions
.start_session(
user.id,
request,
SessionDefaults {
default_locale: self.config.default_locale.clone(),
default_device_platform: self.config.default_device_platform.clone(),
default_app_version: self.config.app_version.clone(),
default_experiment_variant: self.config.default_experiment_variant.clone(),
},
)
.await
}
pub async fn current_session(&self) -> Option<SessionSnapshot> {
self.session.read().await.clone()
self.sessions.current_session().await
}
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)
self.sessions
.end_session()
.await
.map_err(|_| AppError::not_found("No active session"))
}
pub async fn next_event(&self) -> Option<EventSnapshot> {
self.events.next_event().await
}
pub async fn event(&self, event_id: Uuid) -> Option<EventSnapshot> {
self.events.get_event(event_id).await
}
pub async fn event_manifest(&self, event_id: Uuid) -> Option<EventManifestSnapshot> {
self.events.get_manifest(event_id).await
}
pub fn database_ready(&self) -> bool {