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")]
297pub async fn get_credit_balance(
298 data: web::Data<AppState>,
299 auth: AuthenticatedUser,
300 path: web::Path<(Uuid, Uuid)>,
301) -> impl Responder {
302 let (owner_id, building_id) = path.into_inner();
303
304 let owner = match data.owner_use_cases.get_owner(owner_id).await {
306 Ok(Some(owner)) => owner,
307 Ok(None) => {
308 return HttpResponse::NotFound().json(serde_json::json!({
309 "error": format!("Owner not found: {}", owner_id)
310 }))
311 }
312 Err(e) => {
313 return HttpResponse::InternalServerError().json(serde_json::json!({
314 "error": format!("Failed to fetch owner: {}", e)
315 }))
316 }
317 };
318
319 let owner_user_id = owner
320 .user_id
321 .as_ref()
322 .and_then(|id| Uuid::parse_str(id).ok());
323 if owner_user_id != Some(auth.user_id) {
324 return HttpResponse::Forbidden().json(serde_json::json!({
325 "error": "You can only view your own credit balance"
326 }));
327 }
328
329 match data
330 .local_exchange_use_cases
331 .get_credit_balance(owner_id, building_id)
332 .await
333 {
334 Ok(balance) => HttpResponse::Ok().json(balance),
335 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
336 }
337}
338
339#[get("/buildings/{building_id}/leaderboard")]
342pub async fn get_leaderboard(
343 data: web::Data<AppState>,
344 _auth: AuthenticatedUser,
345 building_id: web::Path<Uuid>,
346 query: web::Query<std::collections::HashMap<String, String>>,
347) -> impl Responder {
348 let limit = query
349 .get("limit")
350 .and_then(|l| l.parse::<i32>().ok())
351 .unwrap_or(10);
352
353 match data
354 .local_exchange_use_cases
355 .get_leaderboard(building_id.into_inner(), limit)
356 .await
357 {
358 Ok(leaderboard) => HttpResponse::Ok().json(leaderboard),
359 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
360 }
361}
362
363#[get("/buildings/{building_id}/sel-statistics")]
366pub async fn get_sel_statistics(
367 data: web::Data<AppState>,
368 _auth: AuthenticatedUser,
369 building_id: web::Path<Uuid>,
370) -> impl Responder {
371 match data
372 .local_exchange_use_cases
373 .get_statistics(building_id.into_inner())
374 .await
375 {
376 Ok(stats) => HttpResponse::Ok().json(stats),
377 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
378 }
379}
380
381#[get("/owners/{owner_id}/exchange-summary")]
384pub async fn get_owner_summary(
385 data: web::Data<AppState>,
386 auth: AuthenticatedUser,
387 owner_id: web::Path<Uuid>,
388) -> impl Responder {
389 let owner_id = owner_id.into_inner();
390
391 let owner = match data.owner_use_cases.get_owner(owner_id).await {
393 Ok(Some(owner)) => owner,
394 Ok(None) => {
395 return HttpResponse::NotFound().json(serde_json::json!({
396 "error": format!("Owner not found: {}", owner_id)
397 }))
398 }
399 Err(e) => {
400 return HttpResponse::InternalServerError().json(serde_json::json!({
401 "error": format!("Failed to fetch owner: {}", e)
402 }))
403 }
404 };
405
406 let owner_user_id = owner
407 .user_id
408 .as_ref()
409 .and_then(|id| Uuid::parse_str(id).ok());
410 if owner_user_id != Some(auth.user_id) {
411 return HttpResponse::Forbidden().json(serde_json::json!({
412 "error": "You can only view your own exchange summary"
413 }));
414 }
415
416 match data
417 .local_exchange_use_cases
418 .get_owner_summary(owner_id)
419 .await
420 {
421 Ok(summary) => HttpResponse::Ok().json(summary),
422 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
423 }
424}