ricecoder_sessions/
share.rs1use chrono::{DateTime, Duration, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use uuid::Uuid;
7
8use crate::{Session, SessionError, SessionResult};
9
10#[derive(Debug, Clone)]
12pub struct ShareService {
13 shares: std::sync::Arc<std::sync::Mutex<HashMap<String, SessionShare>>>,
15}
16
17impl ShareService {
18 pub fn new() -> Self {
20 Self {
21 shares: std::sync::Arc::new(std::sync::Mutex::new(HashMap::new())),
22 }
23 }
24
25 pub fn generate_share_link(
27 &self,
28 session_id: &str,
29 permissions: SharePermissions,
30 expires_in: Option<Duration>,
31 ) -> SessionResult<SessionShare> {
32 let share_id = Uuid::new_v4().to_string();
33 let now = Utc::now();
34 let expires_at = expires_in.map(|duration| now + duration);
35
36 let share = SessionShare {
37 id: share_id.clone(),
38 session_id: session_id.to_string(),
39 created_at: now,
40 expires_at,
41 permissions,
42 };
43
44 let mut shares = self
46 .shares
47 .lock()
48 .map_err(|e| SessionError::StorageError(format!("Failed to lock shares: {}", e)))?;
49 shares.insert(share_id.clone(), share.clone());
50
51 Ok(share)
52 }
53
54 pub fn get_share(&self, share_id: &str) -> SessionResult<SessionShare> {
56 let shares = self
57 .shares
58 .lock()
59 .map_err(|e| SessionError::StorageError(format!("Failed to lock shares: {}", e)))?;
60
61 let share = shares
62 .get(share_id)
63 .ok_or_else(|| SessionError::ShareNotFound(share_id.to_string()))?;
64
65 if let Some(expires_at) = share.expires_at {
67 if Utc::now() > expires_at {
68 return Err(SessionError::ShareExpired(share_id.to_string()));
69 }
70 }
71
72 Ok(share.clone())
73 }
74
75 pub fn create_shared_session_view(
77 &self,
78 session: &Session,
79 permissions: &SharePermissions,
80 ) -> Session {
81 let mut shared_session = session.clone();
82
83 if !permissions.include_history {
85 shared_session.history.clear();
86 }
87
88 if !permissions.include_context {
89 shared_session.context.files.clear();
90 shared_session.context.custom.clear();
91 }
92
93 shared_session
94 }
95
96 pub fn import_shared_session(
98 &self,
99 share_id: &str,
100 shared_session: &Session,
101 ) -> SessionResult<Session> {
102 let _share = self.get_share(share_id)?;
104
105 let mut imported_session = shared_session.clone();
107
108 imported_session.id = Uuid::new_v4().to_string();
110
111 let now = Utc::now();
113 imported_session.created_at = now;
114 imported_session.updated_at = now;
115
116 Ok(imported_session)
117 }
118
119 pub fn revoke_share(&self, share_id: &str) -> SessionResult<()> {
121 let mut shares = self
122 .shares
123 .lock()
124 .map_err(|e| SessionError::StorageError(format!("Failed to lock shares: {}", e)))?;
125
126 shares
127 .remove(share_id)
128 .ok_or_else(|| SessionError::ShareNotFound(share_id.to_string()))?;
129
130 Ok(())
131 }
132
133 pub fn list_shares(&self) -> SessionResult<Vec<SessionShare>> {
135 let shares = self
136 .shares
137 .lock()
138 .map_err(|e| SessionError::StorageError(format!("Failed to lock shares: {}", e)))?;
139
140 let now = Utc::now();
141 let active_shares: Vec<SessionShare> = shares
142 .values()
143 .filter(|share| {
144 share.expires_at.is_none() || share.expires_at.is_some_and(|exp| now <= exp)
146 })
147 .cloned()
148 .collect();
149
150 Ok(active_shares)
151 }
152
153 pub fn cleanup_expired_shares(&self) -> SessionResult<usize> {
155 let mut shares = self
156 .shares
157 .lock()
158 .map_err(|e| SessionError::StorageError(format!("Failed to lock shares: {}", e)))?;
159
160 let now = Utc::now();
161 let initial_count = shares.len();
162
163 shares.retain(|_, share| {
164 share.expires_at.is_none() || share.expires_at.is_some_and(|exp| now <= exp)
165 });
166
167 Ok(initial_count - shares.len())
168 }
169
170 pub fn list_shares_for_session(&self, session_id: &str) -> SessionResult<Vec<SessionShare>> {
172 let shares = self
173 .shares
174 .lock()
175 .map_err(|e| SessionError::StorageError(format!("Failed to lock shares: {}", e)))?;
176
177 let now = Utc::now();
178 let session_shares: Vec<SessionShare> = shares
179 .values()
180 .filter(|share| {
181 share.session_id == session_id
183 && (share.expires_at.is_none()
184 || share.expires_at.is_some_and(|exp| now <= exp))
185 })
186 .cloned()
187 .collect();
188
189 Ok(session_shares)
190 }
191
192 pub fn invalidate_session_shares(&self, session_id: &str) -> SessionResult<usize> {
194 let mut shares = self
195 .shares
196 .lock()
197 .map_err(|e| SessionError::StorageError(format!("Failed to lock shares: {}", e)))?;
198
199 let initial_count = shares.len();
200
201 shares.retain(|_, share| share.session_id != session_id);
203
204 Ok(initial_count - shares.len())
205 }
206}
207
208impl Default for ShareService {
209 fn default() -> Self {
210 Self::new()
211 }
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct SessionShare {
217 pub id: String,
219 pub session_id: String,
221 pub created_at: DateTime<Utc>,
223 pub expires_at: Option<DateTime<Utc>>,
225 pub permissions: SharePermissions,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct SharePermissions {
232 pub read_only: bool,
234 pub include_history: bool,
236 pub include_context: bool,
238}