shodh_memory/handlers/
sessions.rs1use axum::{
6 extract::{Path, Query, State},
7 response::Json,
8};
9use serde::{Deserialize, Serialize};
10
11use super::state::MultiUserMemoryManager;
12use crate::errors::{AppError, ValidationErrorExt};
13use crate::memory::{Session, SessionId, SessionStatus, SessionStoreStats, SessionSummary};
14use crate::validation;
15use std::sync::Arc;
16
17type AppState = Arc<MultiUserMemoryManager>;
18
19fn default_sessions_limit() -> usize {
20 10
21}
22
23fn default_end_reason() -> String {
24 "user_ended".to_string()
25}
26
27#[derive(Debug, Deserialize)]
29pub struct ListSessionsRequest {
30 pub user_id: String,
31 #[serde(default = "default_sessions_limit")]
32 pub limit: usize,
33}
34
35#[derive(Debug, Serialize)]
37pub struct ListSessionsResponse {
38 pub success: bool,
39 pub sessions: Vec<SessionSummary>,
40 pub count: usize,
41}
42
43#[derive(Debug, Deserialize)]
45pub struct GetSessionRequest {
46 pub user_id: String,
47}
48
49#[derive(Debug, Serialize)]
51pub struct GetSessionResponse {
52 pub success: bool,
53 pub session: Option<Session>,
54}
55
56#[derive(Debug, Deserialize)]
58pub struct EndSessionRequest {
59 pub user_id: String,
60 #[serde(default = "default_end_reason")]
61 pub reason: String,
62}
63
64#[derive(Debug, Serialize)]
66pub struct EndSessionResponse {
67 pub success: bool,
68 pub session: Option<Session>,
69}
70
71#[derive(Debug, Serialize)]
73pub struct SessionStoreStatsResponse {
74 pub success: bool,
75 pub stats: SessionStoreStats,
76}
77
78pub async fn list_sessions(
80 State(state): State<AppState>,
81 Json(req): Json<ListSessionsRequest>,
82) -> Result<Json<ListSessionsResponse>, AppError> {
83 validation::validate_user_id(&req.user_id).map_validation_err("user_id")?;
84
85 let sessions = state
86 .session_store
87 .get_user_sessions(&req.user_id, req.limit);
88 let count = sessions.len();
89
90 Ok(Json(ListSessionsResponse {
91 success: true,
92 sessions,
93 count,
94 }))
95}
96
97pub async fn get_session(
99 State(state): State<AppState>,
100 Path(session_id): Path<String>,
101 Query(req): Query<GetSessionRequest>,
102) -> Result<Json<GetSessionResponse>, AppError> {
103 validation::validate_user_id(&req.user_id).map_validation_err("user_id")?;
104
105 let uuid = uuid::Uuid::parse_str(&session_id).map_err(|e| AppError::InvalidInput {
106 field: "session_id".to_string(),
107 reason: format!("Invalid UUID: {e}"),
108 })?;
109 let sid = SessionId(uuid);
110 let session = state.session_store.get_session(&sid);
111
112 Ok(Json(GetSessionResponse {
113 success: session.is_some(),
114 session,
115 }))
116}
117
118pub async fn end_session(
120 State(state): State<AppState>,
121 Json(req): Json<EndSessionRequest>,
122) -> Result<Json<EndSessionResponse>, AppError> {
123 validation::validate_user_id(&req.user_id).map_validation_err("user_id")?;
124
125 let sessions = state.session_store.get_user_sessions(&req.user_id, 1);
126 let active_session = sessions
127 .into_iter()
128 .find(|s| matches!(s.status, SessionStatus::Active));
129
130 if let Some(summary) = active_session {
131 let session = state.session_store.end_session(&summary.id, &req.reason);
132 Ok(Json(EndSessionResponse {
133 success: session.is_some(),
134 session,
135 }))
136 } else {
137 Ok(Json(EndSessionResponse {
138 success: false,
139 session: None,
140 }))
141 }
142}
143
144pub async fn get_session_stats(
146 State(state): State<AppState>,
147) -> Result<Json<SessionStoreStatsResponse>, AppError> {
148 let stats = state.session_store.stats();
149
150 Ok(Json(SessionStoreStatsResponse {
151 success: true,
152 stats,
153 }))
154}