koprogo_api/infrastructure/web/handlers/
shared_object_handlers.rs1use crate::application::dto::{BorrowObjectDto, CreateSharedObjectDto, UpdateSharedObjectDto};
2use crate::domain::entities::SharedObjectCategory;
3use crate::infrastructure::web::app_state::AppState;
4use crate::infrastructure::web::middleware::AuthenticatedUser;
5use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
6use uuid::Uuid;
7
8#[post("/shared-objects")]
12pub async fn create_shared_object(
13 data: web::Data<AppState>,
14 auth: AuthenticatedUser,
15 request: web::Json<CreateSharedObjectDto>,
16) -> impl Responder {
17 match data
18 .shared_object_use_cases
19 .create_shared_object(auth.user_id, request.into_inner())
20 .await
21 {
22 Ok(object) => HttpResponse::Created().json(object),
23 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
24 }
25}
26
27#[get("/shared-objects/{id}")]
31pub async fn get_shared_object(data: web::Data<AppState>, id: web::Path<Uuid>) -> impl Responder {
32 match data
33 .shared_object_use_cases
34 .get_shared_object(id.into_inner())
35 .await
36 {
37 Ok(object) => HttpResponse::Ok().json(object),
38 Err(e) => {
39 if e.contains("not found") {
40 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
41 } else {
42 HttpResponse::InternalServerError().json(serde_json::json!({"error": e}))
43 }
44 }
45 }
46}
47
48#[get("/buildings/{building_id}/shared-objects")]
52pub async fn list_building_objects(
53 data: web::Data<AppState>,
54 building_id: web::Path<Uuid>,
55) -> impl Responder {
56 match data
57 .shared_object_use_cases
58 .list_building_objects(building_id.into_inner())
59 .await
60 {
61 Ok(objects) => HttpResponse::Ok().json(objects),
62 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
63 }
64}
65
66#[get("/buildings/{building_id}/shared-objects/available")]
70pub async fn list_available_objects(
71 data: web::Data<AppState>,
72 building_id: web::Path<Uuid>,
73) -> impl Responder {
74 match data
75 .shared_object_use_cases
76 .list_available_objects(building_id.into_inner())
77 .await
78 {
79 Ok(objects) => HttpResponse::Ok().json(objects),
80 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
81 }
82}
83
84#[get("/buildings/{building_id}/shared-objects/borrowed")]
88pub async fn list_borrowed_objects(
89 data: web::Data<AppState>,
90 building_id: web::Path<Uuid>,
91) -> impl Responder {
92 match data
93 .shared_object_use_cases
94 .list_borrowed_objects(building_id.into_inner())
95 .await
96 {
97 Ok(objects) => HttpResponse::Ok().json(objects),
98 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
99 }
100}
101
102#[get("/buildings/{building_id}/shared-objects/overdue")]
106pub async fn list_overdue_objects(
107 data: web::Data<AppState>,
108 building_id: web::Path<Uuid>,
109) -> impl Responder {
110 match data
111 .shared_object_use_cases
112 .list_overdue_objects(building_id.into_inner())
113 .await
114 {
115 Ok(objects) => HttpResponse::Ok().json(objects),
116 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
117 }
118}
119
120#[get("/buildings/{building_id}/shared-objects/free")]
124pub async fn list_free_objects(
125 data: web::Data<AppState>,
126 building_id: web::Path<Uuid>,
127) -> impl Responder {
128 match data
129 .shared_object_use_cases
130 .list_free_objects(building_id.into_inner())
131 .await
132 {
133 Ok(objects) => HttpResponse::Ok().json(objects),
134 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
135 }
136}
137
138#[get("/buildings/{building_id}/shared-objects/category/{category}")]
142pub async fn list_objects_by_category(
143 data: web::Data<AppState>,
144 path: web::Path<(Uuid, String)>,
145) -> impl Responder {
146 let (building_id, category_str) = path.into_inner();
147
148 let category = match serde_json::from_str::<SharedObjectCategory>(&format!(
150 "\"{}\"",
151 category_str
152 )) {
153 Ok(c) => c,
154 Err(_) => {
155 return HttpResponse::BadRequest().json(serde_json::json!({
156 "error": format!("Invalid shared object category: {}. Valid categories: Tools, Books, Electronics, Sports, Gardening, Kitchen, Baby, Other", category_str)
157 }))
158 }
159 };
160
161 match data
162 .shared_object_use_cases
163 .list_objects_by_category(building_id, category)
164 .await
165 {
166 Ok(objects) => HttpResponse::Ok().json(objects),
167 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
168 }
169}
170
171#[get("/owners/{owner_id}/shared-objects")]
175pub async fn list_owner_objects(
176 data: web::Data<AppState>,
177 owner_id: web::Path<Uuid>,
178) -> impl Responder {
179 match data
180 .shared_object_use_cases
181 .list_owner_objects(owner_id.into_inner())
182 .await
183 {
184 Ok(objects) => HttpResponse::Ok().json(objects),
185 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
186 }
187}
188
189#[get("/shared-objects/my-borrowed")]
193pub async fn list_my_borrowed_objects(
194 data: web::Data<AppState>,
195 auth: AuthenticatedUser,
196) -> impl Responder {
197 match data
198 .shared_object_use_cases
199 .list_user_borrowed_objects(auth.user_id)
200 .await
201 {
202 Ok(objects) => HttpResponse::Ok().json(objects),
203 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
204 }
205}
206
207#[put("/shared-objects/{id}")]
211pub async fn update_shared_object(
212 data: web::Data<AppState>,
213 auth: AuthenticatedUser,
214 id: web::Path<Uuid>,
215 request: web::Json<UpdateSharedObjectDto>,
216) -> impl Responder {
217 match data
218 .shared_object_use_cases
219 .update_shared_object(id.into_inner(), auth.user_id, request.into_inner())
220 .await
221 {
222 Ok(object) => HttpResponse::Ok().json(object),
223 Err(e) => {
224 if e.contains("Unauthorized") {
225 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
226 } else if e.contains("not found") {
227 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
228 } else {
229 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
230 }
231 }
232 }
233}
234
235#[post("/shared-objects/{id}/mark-available")]
239pub async fn mark_object_available(
240 data: web::Data<AppState>,
241 auth: AuthenticatedUser,
242 id: web::Path<Uuid>,
243) -> impl Responder {
244 match data
245 .shared_object_use_cases
246 .mark_object_available(id.into_inner(), auth.user_id)
247 .await
248 {
249 Ok(object) => HttpResponse::Ok().json(object),
250 Err(e) => {
251 if e.contains("Unauthorized") {
252 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
253 } else if e.contains("not found") {
254 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
255 } else {
256 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
257 }
258 }
259 }
260}
261
262#[post("/shared-objects/{id}/mark-unavailable")]
266pub async fn mark_object_unavailable(
267 data: web::Data<AppState>,
268 auth: AuthenticatedUser,
269 id: web::Path<Uuid>,
270) -> impl Responder {
271 match data
272 .shared_object_use_cases
273 .mark_object_unavailable(id.into_inner(), auth.user_id)
274 .await
275 {
276 Ok(object) => HttpResponse::Ok().json(object),
277 Err(e) => {
278 if e.contains("Unauthorized") {
279 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
280 } else if e.contains("not found") {
281 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
282 } else {
283 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
284 }
285 }
286 }
287}
288
289#[post("/shared-objects/{id}/borrow")]
293pub async fn borrow_object(
294 data: web::Data<AppState>,
295 auth: AuthenticatedUser,
296 id: web::Path<Uuid>,
297 request: web::Json<BorrowObjectDto>,
298) -> impl Responder {
299 match data
300 .shared_object_use_cases
301 .borrow_object(id.into_inner(), auth.user_id, request.into_inner())
302 .await
303 {
304 Ok(object) => HttpResponse::Ok().json(object),
305 Err(e) => {
306 if e.contains("Owner cannot borrow") {
307 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
308 } else if e.contains("not found") {
309 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
310 } else {
311 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
312 }
313 }
314 }
315}
316
317#[post("/shared-objects/{id}/return")]
321pub async fn return_object(
322 data: web::Data<AppState>,
323 auth: AuthenticatedUser,
324 id: web::Path<Uuid>,
325) -> impl Responder {
326 match data
327 .shared_object_use_cases
328 .return_object(id.into_inner(), auth.user_id)
329 .await
330 {
331 Ok(object) => HttpResponse::Ok().json(object),
332 Err(e) => {
333 if e.contains("Only borrower can return") {
334 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
335 } else if e.contains("not found") {
336 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
337 } else {
338 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
339 }
340 }
341 }
342}
343
344#[delete("/shared-objects/{id}")]
348pub async fn delete_shared_object(
349 data: web::Data<AppState>,
350 auth: AuthenticatedUser,
351 id: web::Path<Uuid>,
352) -> impl Responder {
353 match data
354 .shared_object_use_cases
355 .delete_shared_object(id.into_inner(), auth.user_id)
356 .await
357 {
358 Ok(_) => HttpResponse::NoContent().finish(),
359 Err(e) => {
360 if e.contains("Unauthorized") {
361 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
362 } else if e.contains("not found") {
363 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
364 } else {
365 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
366 }
367 }
368 }
369}
370
371#[get("/buildings/{building_id}/shared-objects/statistics")]
375pub async fn get_object_statistics(
376 data: web::Data<AppState>,
377 building_id: web::Path<Uuid>,
378) -> impl Responder {
379 match data
380 .shared_object_use_cases
381 .get_object_statistics(building_id.into_inner())
382 .await
383 {
384 Ok(stats) => HttpResponse::Ok().json(stats),
385 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
386 }
387}