diff --git a/app/Http/Controllers/HuntsController.php b/app/Http/Controllers/HuntsController.php index a5424a2..4846a2d 100644 --- a/app/Http/Controllers/HuntsController.php +++ b/app/Http/Controllers/HuntsController.php @@ -84,6 +84,245 @@ class HuntsController extends Controller } } + public function store(Request $request): JsonResponse + { + try { + $validated = $request->validate([ + 'title' => 'required|string|max:255', + 'description' => 'nullable|string', + 'city' => 'required|string|max:255', + 'difficulty' => 'required|string', + 'latitude' => 'nullable|numeric', + 'longitude' => 'nullable|numeric', + 'is_public' => 'boolean', + 'estimated_duration_min' => 'nullable|integer', + 'start_at' => 'nullable|date', + 'end_at' => 'nullable|date|after:start_at', + 'image' => 'nullable|string', + ]); + + $validated['creator_id'] = auth('sanctum')->id(); + $validated['slug'] = \Illuminate\Support\Str::slug($validated['title']); + + // Ensure slug is unique + $originalSlug = $validated['slug']; + $counter = 1; + while (Hunts::where('slug', $validated['slug'])->exists()) { + $validated['slug'] = $originalSlug . '-' . $counter; + $counter++; + } + + $hunt = Hunts::create($validated); + + Log::info('HUNT_CREATED', [ + 'hunt_id' => $hunt->id, + 'creator_id' => $validated['creator_id'], + ]); + + return response()->json($hunt, 201); + } catch (\Illuminate\Validation\ValidationException $e) { + return response()->json([ + 'error' => 'Validation Error', + 'messages' => $e->errors(), + ], 422); + } catch (\Throwable $e) { + Log::error('HUNT_STORE_ERROR', [ + 'message' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + return response()->json([ + 'error' => 'Server Error', + 'message' => $e->getMessage(), + ], 500); + } + } + + public function update(Request $request, Hunts $hunt): JsonResponse + { + try { + // Vérifier que l'utilisateur est le créateur ou admin + $user = auth('sanctum')->user(); + if ($hunt->creator_id !== $user->id && !$user->isAdmin()) { + return response()->json([ + 'error' => 'Forbidden', + 'message' => 'You are not authorized to update this hunt', + ], 403); + } + + $validated = $request->validate([ + 'title' => 'sometimes|string|max:255', + 'description' => 'nullable|string', + 'city' => 'sometimes|string|max:255', + 'difficulty' => 'sometimes|string', + 'latitude' => 'nullable|numeric', + 'longitude' => 'nullable|numeric', + 'is_public' => 'boolean', + 'estimated_duration_min' => 'nullable|integer', + 'start_at' => 'nullable|date', + 'end_at' => 'nullable|date|after:start_at', + 'image' => 'nullable|string', + ]); + + // Update slug if title changed + if (isset($validated['title']) && $validated['title'] !== $hunt->title) { + $validated['slug'] = \Illuminate\Support\Str::slug($validated['title']); + + // Ensure slug is unique + $originalSlug = $validated['slug']; + $counter = 1; + while (Hunts::where('slug', $validated['slug'])->where('id', '!=', $hunt->id)->exists()) { + $validated['slug'] = $originalSlug . '-' . $counter; + $counter++; + } + } + + $hunt->update($validated); + + Log::info('HUNT_UPDATED', [ + 'hunt_id' => $hunt->id, + 'updated_by' => $user->id, + ]); + + return response()->json($hunt); + } catch (\Illuminate\Validation\ValidationException $e) { + return response()->json([ + 'error' => 'Validation Error', + 'messages' => $e->errors(), + ], 422); + } catch (\Throwable $e) { + Log::error('HUNT_UPDATE_ERROR', [ + 'hunt_id' => $hunt->id ?? null, + 'message' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + return response()->json([ + 'error' => 'Server Error', + 'message' => $e->getMessage(), + ], 500); + } + } + + public function destroy(Hunts $hunt): JsonResponse + { + try { + // Vérifier que l'utilisateur est le créateur ou admin + $user = auth('sanctum')->user(); + if ($hunt->creator_id !== $user->id && !$user->isAdmin()) { + return response()->json([ + 'error' => 'Forbidden', + 'message' => 'You are not authorized to delete this hunt', + ], 403); + } + + $huntId = $hunt->id; + $hunt->delete(); + + Log::info('HUNT_DELETED', [ + 'hunt_id' => $huntId, + 'deleted_by' => $user->id, + ]); + + return response()->json([ + 'message' => 'Hunt deleted successfully', + ]); + } catch (\Throwable $e) { + Log::error('HUNT_DELETE_ERROR', [ + 'hunt_id' => $hunt->id ?? null, + 'message' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + return response()->json([ + 'error' => 'Server Error', + 'message' => $e->getMessage(), + ], 500); + } + } + + public function participate(Hunts $hunt): JsonResponse + { + try { + $user = auth('sanctum')->user(); + + // Vérifier si l'utilisateur participe déjà + if ($hunt->participants()->where('user_id', $user->id)->exists()) { + return response()->json([ + 'error' => 'Already Participating', + 'message' => 'You are already participating in this hunt', + ], 409); + } + + // Ajouter l'utilisateur comme participant + $hunt->participants()->attach($user->id, [ + 'current_step_number' => 0, + 'status' => 'in_progress', + ]); + + Log::info('HUNT_PARTICIPATE', [ + 'hunt_id' => $hunt->id, + 'user_id' => $user->id, + ]); + + return response()->json([ + 'message' => 'Successfully joined the hunt', + 'hunt' => $hunt->load('participants:id,username,avatar_uri'), + ]); + } catch (\Throwable $e) { + Log::error('HUNT_PARTICIPATE_ERROR', [ + 'hunt_id' => $hunt->id ?? null, + 'user_id' => auth('sanctum')->id(), + 'message' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + return response()->json([ + 'error' => 'Server Error', + 'message' => $e->getMessage(), + ], 500); + } + } + + public function unparticipate(Hunts $hunt): JsonResponse + { + try { + $user = auth('sanctum')->user(); + + // Vérifier si l'utilisateur participe + if (!$hunt->participants()->where('user_id', $user->id)->exists()) { + return response()->json([ + 'error' => 'Not Participating', + 'message' => 'You are not participating in this hunt', + ], 404); + } + + // Retirer l'utilisateur des participants + $hunt->participants()->detach($user->id); + + Log::info('HUNT_UNPARTICIPATE', [ + 'hunt_id' => $hunt->id, + 'user_id' => $user->id, + ]); + + return response()->json([ + 'message' => 'Successfully left the hunt', + ]); + } catch (\Throwable $e) { + Log::error('HUNT_UNPARTICIPATE_ERROR', [ + 'hunt_id' => $hunt->id ?? null, + 'user_id' => auth('sanctum')->id(), + 'message' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + return response()->json([ + 'error' => 'Server Error', + 'message' => $e->getMessage(), + ], 500); + } + } + private function buildMeilisearchFilters(Request $request): string { $filters = []; diff --git a/app/Models/User.php b/app/Models/User.php index 719b5ba..9d4b71d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -130,4 +130,14 @@ class User extends Authenticatable implements FilamentUser, HasName { return $this->role === UserRole::ADMIN || $this->role === UserRole::ORGA; } + + public function isAdmin(): bool + { + return $this->role === UserRole::ADMIN; + } + + public function isOrga(): bool + { + return $this->role === UserRole::ORGA; + } }