validated(); $identifier = strtolower(trim($credentials['identifier'])); $user = $this->findUserForLogin($identifier); if (! $user || ! Hash::check($credentials['password'], $user->password)) { $this->logAuthEvent(null, 'failed_login', 'تلاش ناموفق ورود', $request, $identifier); return response()->json([ 'success' => false, 'message' => 'نام کاربری یا رمز عبور اشتباه است', ], 401); } if ($user->status !== null && $user->status !== 'active') { $this->logAuthEvent($user->id, 'blocked_login', 'تلاش ورود کاربر غیرفعال', $request, $identifier); return response()->json([ 'success' => false, 'message' => 'امکان ورود با این اطلاعات وجود ندارد', ], 403); } $token = $user->createToken('api-token')->plainTextToken; $user->load(['roles.permissions']); $this->logAuthEvent($user->id, 'login', 'ورود موفق به سیستم', $request, $identifier); return response()->json([ 'success' => true, 'data' => [ 'user' => new UserResource($user), 'token' => $token, 'permissions' => $user->roles->flatMap(function ($role) { return $role->permissions->pluck('name'); })->unique()->values(), ], 'message' => 'ورود با موفقیت انجام شد', ]); } catch (\Exception $e) { Log::error('Login failed unexpectedly', [ 'identifier_hash' => isset($identifier) ? hash('sha256', $identifier) : null, 'exception' => $e, ]); return response()->json([ 'success' => false, 'message' => 'خطا در ورود به سیستم', ], 500); } } private function findUserForLogin(string $identifier): ?User { $query = User::query() ->whereRaw('LOWER(email) = ?', [$identifier]) ->orWhereRaw('LOWER(name) = ?', [$identifier]); if (Schema::hasColumn('users', 'username')) { $query->orWhereRaw('LOWER(username) = ?', [$identifier]); } $user = $query->orderBy('id')->first(); if ($user || str_contains($identifier, '@')) { return $user; } $emailPrefixMatches = User::query() ->whereRaw('LOWER(email) LIKE ?', [$identifier.'@%']) ->limit(2) ->get(); return $emailPrefixMatches->count() === 1 ? $emailPrefixMatches->first() : null; } public function logout(Request $request): JsonResponse { try { $this->logAuthEvent($request->user()?->id, 'logout', 'خروج از سیستم', $request); $request->user()->currentAccessToken()?->delete(); return response()->json([ 'success' => true, 'message' => 'خروج با موفقیت انجام شد', ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در خروج از سیستم', ], 500); } } public function user(Request $request): JsonResponse { try { $user = $request->user()->load(['roles.permissions']); return response()->json([ 'success' => true, 'data' => new UserResource($user), 'message' => 'اطلاعات کاربر', ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در دریافت اطلاعات کاربر', ], 500); } } public function updateProfile(UpdateProfileRequest $request): JsonResponse { try { $user = $request->user(); $user->update($request->validated()); return response()->json([ 'success' => true, 'data' => new UserResource($user->fresh()), 'message' => 'پروفایل با موفقیت به‌روزرسانی شد', ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در به‌روزرسانی پروفایل', ], 500); } } public function updateAvatar(Request $request): JsonResponse { try { $request->validate([ 'avatar' => 'required|image|mimes:jpg,jpeg,png,webp|max:2048', ]); $user = $request->user(); if ($user->avatar && $user->avatar !== 'avatars/default.png' && Storage::disk('public')->exists($user->avatar)) { Storage::disk('public')->delete($user->avatar); } $path = $request->file('avatar')->store('avatars', 'public'); $user->update(['avatar' => $path]); return response()->json([ 'success' => true, 'data' => new UserResource($user->fresh()), 'message' => 'عکس پروفایل با موفقیت به‌روزرسانی شد', ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در به‌روزرسانی عکس پروفایل', ], 500); } } public function changePassword(ChangePasswordRequest $request): JsonResponse { try { $user = $request->user(); if (! Hash::check($request->current_password, $user->password)) { return response()->json([ 'success' => false, 'message' => 'رمز عبور فعلی اشتباه است', ], 400); } $user->update(['password' => Hash::make($request->new_password)]); $user->tokens()->delete(); return response()->json([ 'success' => true, 'message' => 'رمز عبور با موفقیت تغییر یافت', 'reauthentication_required' => true, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در تغییر رمز عبور', ], 500); } } public function forgotPassword(Request $request): JsonResponse { $data = $request->validate(['email' => 'required|email']); Password::sendResetLink(['email' => strtolower($data['email'])]); return response()->json([ 'success' => true, 'message' => 'اگر حسابی با این ایمیل وجود داشته باشد، لینک بازیابی ارسال می‌شود.', ]); } public function resetPassword(Request $request): JsonResponse { $data = $request->validate([ 'token' => 'required|string', 'email' => 'required|email', 'password' => 'required|string|min:12|confirmed', ]); $status = Password::reset( $data, function (User $user, string $password) { $user->forceFill([ 'password' => Hash::make($password), 'remember_token' => Str::random(60), ])->save(); $user->tokens()->delete(); event(new PasswordReset($user)); } ); if ($status !== Password::PASSWORD_RESET) { return response()->json([ 'success' => false, 'message' => 'توکن بازیابی نامعتبر یا منقضی شده است.', ], 422); } return response()->json([ 'success' => true, 'message' => 'رمز عبور تغییر کرد. دوباره وارد شوید.', ]); } private function logAuthEvent(?int $userId, string $action, string $description, Request $request, ?string $identifier = null): void { try { $properties = [ 'ip' => $request->ip(), 'user_agent' => substr((string) $request->userAgent(), 0, 255), ]; if ($identifier) { $properties['identifier_hash'] = hash('sha256', strtolower($identifier)); } $this->activityLogService->log($userId, $action, $description, 'auth', $userId, null, null, $properties); } catch (\Throwable $e) { Log::warning('Auth activity log failed', [ 'user_id' => $userId, 'action' => $action, 'exception' => $e, ]); } } }