koprogo_api/infrastructure/web/handlers/
ag_session_handlers.rs1use crate::application::dto::ag_session_dto::{
2 CreateAgSessionDto, EndAgSessionDto, RecordRemoteJoinDto,
3};
4use crate::infrastructure::web::{AppState, AuthenticatedUser};
5use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
6use uuid::Uuid;
7
8#[post("/meetings/{meeting_id}/ag-session")]
10pub async fn create_ag_session(
11 state: web::Data<AppState>,
12 user: AuthenticatedUser,
13 path: web::Path<Uuid>,
14 body: web::Json<CreateAgSessionDto>,
15) -> impl Responder {
16 let organization_id = match user.require_organization() {
17 Ok(id) => id,
18 Err(e) => {
19 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
20 }
21 };
22
23 let mut dto = body.into_inner();
24 dto.meeting_id = path.into_inner();
25
26 match state
27 .ag_session_use_cases
28 .create_session(organization_id, dto, user.user_id)
29 .await
30 {
31 Ok(session) => HttpResponse::Created().json(session),
32 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
33 }
34}
35
36#[get("/meetings/{meeting_id}/ag-session")]
38pub async fn get_ag_session_for_meeting(
39 state: web::Data<AppState>,
40 user: AuthenticatedUser,
41 path: web::Path<Uuid>,
42) -> impl Responder {
43 let organization_id = match user.require_organization() {
44 Ok(id) => id,
45 Err(e) => {
46 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
47 }
48 };
49
50 match state
51 .ag_session_use_cases
52 .get_session_for_meeting(path.into_inner(), organization_id)
53 .await
54 {
55 Ok(Some(session)) => HttpResponse::Ok().json(session),
56 Ok(None) => HttpResponse::NotFound()
57 .json(serde_json::json!({"error": "Aucune session visio pour cette réunion"})),
58 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
59 }
60}
61
62#[get("/ag-sessions")]
64pub async fn list_ag_sessions(
65 state: web::Data<AppState>,
66 user: AuthenticatedUser,
67) -> impl Responder {
68 let organization_id = match user.require_organization() {
69 Ok(id) => id,
70 Err(e) => {
71 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
72 }
73 };
74
75 match state
76 .ag_session_use_cases
77 .list_sessions(organization_id)
78 .await
79 {
80 Ok(sessions) => HttpResponse::Ok().json(sessions),
81 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
82 }
83}
84
85#[get("/ag-sessions/{id}")]
87pub async fn get_ag_session(
88 state: web::Data<AppState>,
89 user: AuthenticatedUser,
90 path: web::Path<Uuid>,
91) -> impl Responder {
92 let organization_id = match user.require_organization() {
93 Ok(id) => id,
94 Err(e) => {
95 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
96 }
97 };
98
99 match state
100 .ag_session_use_cases
101 .get_session(path.into_inner(), organization_id)
102 .await
103 {
104 Ok(session) => HttpResponse::Ok().json(session),
105 Err(e) => {
106 if e.contains("introuvable") {
107 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
108 } else if e.contains("refusé") {
109 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
110 } else {
111 HttpResponse::InternalServerError().json(serde_json::json!({"error": e}))
112 }
113 }
114 }
115}
116
117#[put("/ag-sessions/{id}/start")]
119pub async fn start_ag_session(
120 state: web::Data<AppState>,
121 user: AuthenticatedUser,
122 path: web::Path<Uuid>,
123) -> impl Responder {
124 let organization_id = match user.require_organization() {
125 Ok(id) => id,
126 Err(e) => {
127 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
128 }
129 };
130
131 match state
132 .ag_session_use_cases
133 .start_session(path.into_inner(), organization_id)
134 .await
135 {
136 Ok(session) => HttpResponse::Ok().json(session),
137 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
138 }
139}
140
141#[put("/ag-sessions/{id}/end")]
143pub async fn end_ag_session(
144 state: web::Data<AppState>,
145 user: AuthenticatedUser,
146 path: web::Path<Uuid>,
147 body: web::Json<EndAgSessionDto>,
148) -> impl Responder {
149 let organization_id = match user.require_organization() {
150 Ok(id) => id,
151 Err(e) => {
152 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
153 }
154 };
155
156 match state
157 .ag_session_use_cases
158 .end_session(path.into_inner(), organization_id, body.into_inner())
159 .await
160 {
161 Ok(session) => HttpResponse::Ok().json(session),
162 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
163 }
164}
165
166#[put("/ag-sessions/{id}/cancel")]
168pub async fn cancel_ag_session(
169 state: web::Data<AppState>,
170 user: AuthenticatedUser,
171 path: web::Path<Uuid>,
172) -> impl Responder {
173 let organization_id = match user.require_organization() {
174 Ok(id) => id,
175 Err(e) => {
176 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
177 }
178 };
179
180 match state
181 .ag_session_use_cases
182 .cancel_session(path.into_inner(), organization_id)
183 .await
184 {
185 Ok(session) => HttpResponse::Ok().json(session),
186 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
187 }
188}
189
190#[post("/ag-sessions/{id}/join")]
192pub async fn record_remote_join(
193 state: web::Data<AppState>,
194 user: AuthenticatedUser,
195 path: web::Path<Uuid>,
196 body: web::Json<RecordRemoteJoinDto>,
197) -> impl Responder {
198 let organization_id = match user.require_organization() {
199 Ok(id) => id,
200 Err(e) => {
201 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
202 }
203 };
204
205 match state
206 .ag_session_use_cases
207 .record_remote_join(path.into_inner(), organization_id, body.into_inner())
208 .await
209 {
210 Ok(session) => HttpResponse::Ok().json(session),
211 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
212 }
213}
214
215#[get("/ag-sessions/{id}/quorum")]
217pub async fn get_combined_quorum(
218 state: web::Data<AppState>,
219 user: AuthenticatedUser,
220 path: web::Path<Uuid>,
221 query: web::Query<CombinedQuorumQuery>,
222) -> impl Responder {
223 let organization_id = match user.require_organization() {
224 Ok(id) => id,
225 Err(e) => {
226 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
227 }
228 };
229
230 match state
231 .ag_session_use_cases
232 .calculate_combined_quorum(
233 path.into_inner(),
234 organization_id,
235 query.physical_quotas,
236 query.total_building_quotas,
237 )
238 .await
239 {
240 Ok(result) => HttpResponse::Ok().json(result),
241 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
242 }
243}
244
245#[delete("/ag-sessions/{id}")]
247pub async fn delete_ag_session(
248 state: web::Data<AppState>,
249 user: AuthenticatedUser,
250 path: web::Path<Uuid>,
251) -> impl Responder {
252 let organization_id = match user.require_organization() {
253 Ok(id) => id,
254 Err(e) => {
255 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
256 }
257 };
258
259 match state
260 .ag_session_use_cases
261 .delete_session(path.into_inner(), organization_id)
262 .await
263 {
264 Ok(()) => HttpResponse::NoContent().finish(),
265 Err(e) => {
266 if e.contains("introuvable") {
267 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
268 } else if e.contains("refusé") {
269 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
270 } else {
271 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
272 }
273 }
274 }
275}
276
277#[get("/ag-sessions/platform-stats")]
280pub async fn get_ag_session_platform_stats(
281 state: web::Data<AppState>,
282 user: AuthenticatedUser,
283) -> impl Responder {
284 if user.role != "SUPERADMIN" {
286 return HttpResponse::Forbidden().json(serde_json::json!({
287 "error": "SuperAdmin only"
288 }));
289 }
290
291 match state.ag_session_use_cases.list_pending_sessions().await {
293 Ok(pending) => HttpResponse::Ok().json(serde_json::json!({
294 "pending_sessions_count": pending.len(),
295 "note": "Platform-wide statistics endpoint - detailed metrics coming soon"
296 })),
297 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
298 }
299}
300
301#[derive(serde::Deserialize)]
302pub struct CombinedQuorumQuery {
303 pub physical_quotas: f64,
304 pub total_building_quotas: f64,
305}