koprogo_api/infrastructure/web/handlers/
local_exchange_handlers.rs1use crate::application::dto::{
2 CancelExchangeDto, CompleteExchangeDto, CreateLocalExchangeDto, RateExchangeDto,
3 RequestExchangeDto,
4};
5use crate::domain::entities::ExchangeType;
6use crate::infrastructure::web::{AppState, AuthenticatedUser};
7use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
8use uuid::Uuid;
9
10#[post("/exchanges")]
13pub async fn create_exchange(
14 data: web::Data<AppState>,
15 auth: AuthenticatedUser,
16 request: web::Json<CreateLocalExchangeDto>,
17) -> impl Responder {
18 match data
19 .local_exchange_use_cases
20 .create_exchange(auth.user_id, request.into_inner())
21 .await
22 {
23 Ok(exchange) => HttpResponse::Created().json(exchange),
24 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
25 }
26}
27
28#[get("/exchanges/{id}")]
31pub async fn get_exchange(
32 data: web::Data<AppState>,
33 _auth: AuthenticatedUser,
34 id: web::Path<Uuid>,
35) -> impl Responder {
36 match data
37 .local_exchange_use_cases
38 .get_exchange(id.into_inner())
39 .await
40 {
41 Ok(exchange) => HttpResponse::Ok().json(exchange),
42 Err(e) => HttpResponse::NotFound().json(serde_json::json!({"error": e})),
43 }
44}
45
46#[get("/buildings/{building_id}/exchanges")]
49pub async fn list_building_exchanges(
50 data: web::Data<AppState>,
51 _auth: AuthenticatedUser,
52 building_id: web::Path<Uuid>,
53) -> impl Responder {
54 match data
55 .local_exchange_use_cases
56 .list_building_exchanges(building_id.into_inner())
57 .await
58 {
59 Ok(exchanges) => HttpResponse::Ok().json(exchanges),
60 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
61 }
62}
63
64#[get("/buildings/{building_id}/exchanges/available")]
67pub async fn list_available_exchanges(
68 data: web::Data<AppState>,
69 _auth: AuthenticatedUser,
70 building_id: web::Path<Uuid>,
71) -> impl Responder {
72 match data
73 .local_exchange_use_cases
74 .list_available_exchanges(building_id.into_inner())
75 .await
76 {
77 Ok(exchanges) => HttpResponse::Ok().json(exchanges),
78 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
79 }
80}
81
82#[get("/owners/{owner_id}/exchanges")]
85pub async fn list_owner_exchanges(
86 data: web::Data<AppState>,
87 auth: AuthenticatedUser,
88 owner_id: web::Path<Uuid>,
89) -> impl Responder {
90 let owner_id = owner_id.into_inner();
91
92 let owner = match data.owner_use_cases.get_owner(owner_id).await {
95 Ok(Some(owner)) => owner,
96 Ok(None) => {
97 return HttpResponse::NotFound().json(serde_json::json!({
98 "error": format!("Owner not found: {}", owner_id)
99 }))
100 }
101 Err(e) => {
102 return HttpResponse::InternalServerError().json(serde_json::json!({
103 "error": format!("Failed to fetch owner: {}", e)
104 }))
105 }
106 };
107
108 let owner_user_id = owner
110 .user_id
111 .as_ref()
112 .and_then(|id| Uuid::parse_str(id).ok());
113 if owner_user_id != Some(auth.user_id) {
114 return HttpResponse::Forbidden().json(serde_json::json!({
115 "error": "You can only view your own exchanges"
116 }));
117 }
118
119 match data
120 .local_exchange_use_cases
121 .list_owner_exchanges(owner_id)
122 .await
123 {
124 Ok(exchanges) => HttpResponse::Ok().json(exchanges),
125 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
126 }
127}
128
129#[get("/buildings/{building_id}/exchanges/type/{exchange_type}")]
132pub async fn list_exchanges_by_type(
133 data: web::Data<AppState>,
134 _auth: AuthenticatedUser,
135 path: web::Path<(Uuid, String)>,
136) -> impl Responder {
137 let (building_id, exchange_type_str) = path.into_inner();
138
139 let exchange_type = match exchange_type_str.as_str() {
141 "Service" => ExchangeType::Service,
142 "ObjectLoan" => ExchangeType::ObjectLoan,
143 "SharedPurchase" => ExchangeType::SharedPurchase,
144 _ => {
145 return HttpResponse::BadRequest().json(serde_json::json!({
146 "error": "Invalid exchange type. Must be Service, ObjectLoan, or SharedPurchase"
147 }));
148 }
149 };
150
151 match data
152 .local_exchange_use_cases
153 .list_exchanges_by_type(building_id, exchange_type)
154 .await
155 {
156 Ok(exchanges) => HttpResponse::Ok().json(exchanges),
157 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
158 }
159}
160
161#[post("/exchanges/{id}/request")]
164pub async fn request_exchange(
165 data: web::Data<AppState>,
166 auth: AuthenticatedUser,
167 id: web::Path<Uuid>,
168 request: web::Json<RequestExchangeDto>,
169) -> impl Responder {
170 match data
171 .local_exchange_use_cases
172 .request_exchange(id.into_inner(), auth.user_id, request.into_inner())
173 .await
174 {
175 Ok(exchange) => HttpResponse::Ok().json(exchange),
176 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
177 }
178}
179
180#[post("/exchanges/{id}/start")]
184pub async fn start_exchange(
185 data: web::Data<AppState>,
186 auth: AuthenticatedUser,
187 id: web::Path<Uuid>,
188) -> impl Responder {
189 match data
190 .local_exchange_use_cases
191 .start_exchange(id.into_inner(), auth.user_id)
192 .await
193 {
194 Ok(exchange) => HttpResponse::Ok().json(exchange),
195 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
196 }
197}
198
199#[post("/exchanges/{id}/complete")]
203pub async fn complete_exchange(
204 data: web::Data<AppState>,
205 auth: AuthenticatedUser,
206 id: web::Path<Uuid>,
207 request: web::Json<CompleteExchangeDto>,
208) -> impl Responder {
209 match data
210 .local_exchange_use_cases
211 .complete_exchange(id.into_inner(), auth.user_id, request.into_inner())
212 .await
213 {
214 Ok(exchange) => HttpResponse::Ok().json(exchange),
215 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
216 }
217}
218
219#[post("/exchanges/{id}/cancel")]
222pub async fn cancel_exchange(
223 data: web::Data<AppState>,
224 auth: AuthenticatedUser,
225 id: web::Path<Uuid>,
226 request: web::Json<CancelExchangeDto>,
227) -> impl Responder {
228 match data
229 .local_exchange_use_cases
230 .cancel_exchange(id.into_inner(), auth.user_id, request.into_inner())
231 .await
232 {
233 Ok(exchange) => HttpResponse::Ok().json(exchange),
234 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
235 }
236}
237
238#[put("/exchanges/{id}/rate-provider")]
241pub async fn rate_provider(
242 data: web::Data<AppState>,
243 auth: AuthenticatedUser,
244 id: web::Path<Uuid>,
245 request: web::Json<RateExchangeDto>,
246) -> impl Responder {
247 match data
248 .local_exchange_use_cases
249 .rate_provider(id.into_inner(), auth.user_id, request.into_inner())
250 .await
251 {
252 Ok(exchange) => HttpResponse::Ok().json(exchange),
253 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
254 }
255}
256
257#[put("/exchanges/{id}/rate-requester")]
260pub async fn rate_requester(
261 data: web::Data<AppState>,
262 auth: AuthenticatedUser,
263 id: web::Path<Uuid>,
264 request: web::Json<RateExchangeDto>,
265) -> impl Responder {
266 match data
267 .local_exchange_use_cases
268 .rate_requester(id.into_inner(), auth.user_id, request.into_inner())
269 .await
270 {
271 Ok(exchange) => HttpResponse::Ok().json(exchange),
272 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
273 }
274}
275
276#[delete("/exchanges/{id}")]
279pub async fn delete_exchange(
280 data: web::Data<AppState>,
281 auth: AuthenticatedUser,
282 id: web::Path<Uuid>,
283) -> impl Responder {
284 match data
285 .local_exchange_use_cases
286 .delete_exchange(id.into_inner(), auth.user_id)
287 .await
288 {
289 Ok(_) => HttpResponse::NoContent().finish(),
290 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
291 }
292}
293
294#[get("/owners/{owner_id}/buildings/{building_id}/credit-balance")]
298pub async fn get_credit_balance(
299 data: web::Data<AppState>,
300 auth: AuthenticatedUser,
301 path: web::Path<(Uuid, Uuid)>,
302) -> impl Responder {
303 let (path_id, building_id) = path.into_inner();
304
305 let owner = match data.owner_use_cases.get_owner(path_id).await {
308 Ok(Some(owner)) => owner,
309 Ok(None) => {
310 match data.owner_use_cases.find_owner_by_user_id(path_id).await {
312 Ok(Some(owner)) => owner,
313 Ok(None) => {
314 return HttpResponse::NotFound().json(serde_json::json!({
315 "error": format!("Owner not found for id: {}", path_id)
316 }))
317 }
318 Err(e) => {
319 return HttpResponse::InternalServerError().json(serde_json::json!({
320 "error": format!("Failed to fetch owner: {}", e)
321 }))
322 }
323 }
324 }
325 Err(e) => {
326 return HttpResponse::InternalServerError().json(serde_json::json!({
327 "error": format!("Failed to fetch owner: {}", e)
328 }))
329 }
330 };
331
332 let owner_user_id = owner
334 .user_id
335 .as_ref()
336 .and_then(|id| Uuid::parse_str(id).ok());
337 if owner_user_id != Some(auth.user_id) {
338 return HttpResponse::Forbidden().json(serde_json::json!({
339 "error": "You can only view your own credit balance"
340 }));
341 }
342
343 let owner_uuid = match Uuid::parse_str(&owner.id) {
344 Ok(id) => id,
345 Err(_) => {
346 return HttpResponse::InternalServerError().json(serde_json::json!({
347 "error": "Invalid owner ID format"
348 }))
349 }
350 };
351
352 match data
353 .local_exchange_use_cases
354 .get_credit_balance(owner_uuid, building_id)
355 .await
356 {
357 Ok(balance) => HttpResponse::Ok().json(balance),
358 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
359 }
360}
361
362#[get("/buildings/{building_id}/leaderboard")]
365pub async fn get_leaderboard(
366 data: web::Data<AppState>,
367 _auth: AuthenticatedUser,
368 building_id: web::Path<Uuid>,
369 query: web::Query<std::collections::HashMap<String, String>>,
370) -> impl Responder {
371 let limit = query
372 .get("limit")
373 .and_then(|l| l.parse::<i32>().ok())
374 .unwrap_or(10);
375
376 match data
377 .local_exchange_use_cases
378 .get_leaderboard(building_id.into_inner(), limit)
379 .await
380 {
381 Ok(leaderboard) => HttpResponse::Ok().json(leaderboard),
382 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
383 }
384}
385
386#[get("/buildings/{building_id}/sel-statistics")]
389pub async fn get_sel_statistics(
390 data: web::Data<AppState>,
391 _auth: AuthenticatedUser,
392 building_id: web::Path<Uuid>,
393) -> impl Responder {
394 match data
395 .local_exchange_use_cases
396 .get_statistics(building_id.into_inner())
397 .await
398 {
399 Ok(stats) => HttpResponse::Ok().json(stats),
400 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
401 }
402}
403
404#[get("/owners/{owner_id}/exchange-summary")]
407pub async fn get_owner_summary(
408 data: web::Data<AppState>,
409 auth: AuthenticatedUser,
410 owner_id: web::Path<Uuid>,
411) -> impl Responder {
412 let owner_id = owner_id.into_inner();
413
414 let owner = match data.owner_use_cases.get_owner(owner_id).await {
416 Ok(Some(owner)) => owner,
417 Ok(None) => {
418 return HttpResponse::NotFound().json(serde_json::json!({
419 "error": format!("Owner not found: {}", owner_id)
420 }))
421 }
422 Err(e) => {
423 return HttpResponse::InternalServerError().json(serde_json::json!({
424 "error": format!("Failed to fetch owner: {}", e)
425 }))
426 }
427 };
428
429 let owner_user_id = owner
430 .user_id
431 .as_ref()
432 .and_then(|id| Uuid::parse_str(id).ok());
433 if owner_user_id != Some(auth.user_id) {
434 return HttpResponse::Forbidden().json(serde_json::json!({
435 "error": "You can only view your own exchange summary"
436 }));
437 }
438
439 match data
440 .local_exchange_use_cases
441 .get_owner_summary(owner_id)
442 .await
443 {
444 Ok(summary) => HttpResponse::Ok().json(summary),
445 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
446 }
447}