validate([ 'phone' => 'required|string', 'password' => 'required', ]); $login = $this->normalizeLoginIdentifier($request->phone); $user = str_contains($login, '@') ? User::whereRaw('lower(email) = ?', [$login])->first() : User::get()->first(fn (User $candidate) => $this->normalizeLoginIdentifier($candidate->phone) === $login); if (! $user || ! Hash::check($request->password, $user->password)) { throw ValidationException::withMessages([ 'phone' => ['شماره موبایل یا رمز عبور اشتباه است'], ]); } if (! $user->is_active) { return response()->json(['message' => 'حساب کاربری شما غیرفعال است'], 403); } Auth::guard('web')->login($user); $request->session()->regenerate(); $user->update([ 'last_login_at' => now(), 'last_login_ip' => $request->ip(), 'current_login_at' => now(), ]); ActivityLogger::log('login', "User {$user->phone} logged in"); $user->load('roles.permissions', 'teams'); return response()->json([ 'data' => $user, ]); } public function logout(Request $request): JsonResponse { $user = $request->user(); if ($user?->current_login_at) { $user->increment('total_presence_seconds', max(0, $user->current_login_at->diffInSeconds(now()))); $user->forceFill([ 'current_login_at' => null, 'last_logout_at' => now(), ])->save(); } ActivityLogger::log('logout', 'User logged out'); Auth::guard('web')->logout(); if ($request->hasSession()) { $request->session()->invalidate(); $request->session()->regenerateToken(); } return response()->json(['message' => 'خروج با موفقیت انجام شد']); } public function me(Request $request): JsonResponse { $user = Auth::guard('web')->user(); if (! $user) { return response()->json([ 'authenticated' => false, 'data' => null, ]); } $user->load('roles.permissions', 'teams'); $user->permissions = $user->getAllPermissions()->pluck('name'); return response()->json([ 'authenticated' => true, 'data' => $user, ]); } public function updateProfile(Request $request): JsonResponse { $user = $request->user(); $validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users,email,'.$user->id, 'phone' => 'nullable|string|max:20|unique:users,phone,'.$user->id, 'password' => ['nullable', ...PasswordPolicy::rules()], 'avatar' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048', ]); if ($request->hasFile('avatar')) { $path = $request->file('avatar')->store('avatars', 'public'); $validated['avatar'] = $path; } if (! empty($validated['password'])) { $validated['password'] = Hash::make($validated['password']); } else { unset($validated['password']); } $user->update($validated); $user->load('roles.permissions', 'teams'); $user->permissions = $user->getAllPermissions()->pluck('name'); ActivityLogger::log('profile_updated', "User {$user->phone} updated profile"); return response()->json(['data' => $user]); } private function normalizeLoginIdentifier(?string $value): string { $value = trim((string) $value); if (str_contains($value, '@')) { return strtolower($value); } $value = strtr($value, [ '۰' => '0', '۱' => '1', '۲' => '2', '۳' => '3', '۴' => '4', '۵' => '5', '۶' => '6', '۷' => '7', '۸' => '8', '۹' => '9', '٠' => '0', '١' => '1', '٢' => '2', '٣' => '3', '٤' => '4', '٥' => '5', '٦' => '6', '٧' => '7', '٨' => '8', '٩' => '9', ]); return preg_replace('/\D+/', '', $value) ?? ''; } }