/
itp_practice
/
itp_backend
Обзор
Документация
Войти
/
itp_practice
/
itp_backend
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
app/Services/Auth/TelegramAuthService.php
234 строки
8 KB
vivanenko
tg register
29 май 2025, 19:11
29 май 2025, 19:11
94c4ab4
Код
Авторство
О чём код?
<?php namespace App\Services\Auth; use App\Config\TelegramConfig; use App\Contracts\Auth\TelegramAuthenticationService; use App\Contracts\Telegram\TelegramBotService; use App\DTOs\Auth\TelegramAuthData; use App\DTOs\Auth\TelegramAuthResult; use App\Models\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Log; use RuntimeException; class TelegramAuthService implements TelegramAuthenticationService { public function __construct( private readonly TelegramBotService $telegramBot, private readonly TelegramConfig $config ) { } /** * Основной метод авторизации через Telegram * Следует принципу SOLID - Single Responsibility */ public function authenticate(TelegramAuthData $authData): TelegramAuthResult { // Business Logic Layer - поиск или создание пользователя $user = $this->findOrCreateUser($authData); // Data Access Layer - создание токена $token = $this->createUserToken($user); // Business Logic Layer - отправка welcome сообщения для новых пользователей if ($user->wasRecentlyCreated) { $this->sendWelcomeMessage($authData->telegramId); } // Business Logic Layer - логирование успешной авторизации $this->logSuccessfulAuth($user, $authData); return new TelegramAuthResult( user: $user, token: $token, isNewUser: $user->wasRecentlyCreated ); } /** * Data Access Layer - поиск или создание пользователя */ private function findOrCreateUser(TelegramAuthData $authData): User { // Сначала ищем пользователя по telegram_id $user = User::where('telegram_id', $authData->telegramId)->first(); if ($user) { // Пользователь существует - обновляем его данные если нужно $this->updateExistingUser($user, $authData); return $user; } // Пользователь не найден - создаем нового return $this->createNewUser($authData); } /** * Business Logic Layer - обновление существующего пользователя */ private function updateExistingUser(User $user, TelegramAuthData $authData): void { $updateData = []; // Обновляем имя если изменилось $newName = $authData->getFullName(); if ($user->name !== $newName) { $updateData['name'] = $newName; } // Обновляем username если изменилось if ($user->telegram_username !== $authData->username) { $updateData['telegram_username'] = $authData->username; } // Обновляем telegram_data с последними данными $updateData['telegram_data'] = $authData->rawData; $updateData['last_login'] = now(); // Убеждаемся что Telegram верифицирован if (!$user->is_telegram_verified) { $updateData['is_telegram_verified'] = true; } $user->remember_token = $authData->startToken; if (!empty($updateData)) { $user->update($updateData); Log::info('Updated existing Telegram user', [ 'user_id' => $user->id, 'telegram_id' => $authData->telegramId, 'updated_fields' => array_keys($updateData), ]); } } /** * Data Access Layer - создание нового пользователя */ private function createNewUser(TelegramAuthData $authData): User { $userData = [ 'name' => $authData->getFullName(), 'email' => $authData->getTempEmail(), 'password' => Hash::make(uniqid()), // Случайный пароль 'telegram_id' => $authData->telegramId, 'telegram_username' => $authData->username, 'telegram_data' => $authData->rawData, 'is_telegram_verified' => true, 'remember_token' => $authData->startToken, 'last_login' => now(), ]; $user = User::create($userData); Log::info('Created new Telegram user', [ 'user_id' => $user->id, 'telegram_id' => $authData->telegramId, 'username' => $authData->username, 'has_start_token' => $authData->hasStartToken(), ]); return $user; } /** * Data Access Layer - создание токена для пользователя */ private function createUserToken(User $user): string { // Создаем токен с понятным именем $tokenName = 'telegram_auth_' . now()->format('Y-m-d_H:i:s'); return $user->createToken($tokenName)->plainTextToken; } /** * Business Logic Layer - логирование успешной авторизации */ private function logSuccessfulAuth(User $user, TelegramAuthData $authData): void { Log::info('Successful Telegram authentication', [ 'user_id' => $user->id, 'telegram_id' => $authData->telegramId, 'username' => $authData->username, 'was_recently_created' => $user->wasRecentlyCreated, 'has_start_token' => $authData->hasStartToken(), 'start_token' => $authData->startToken, // Для отладки ]); } /** * Валидация данных авторизации (legacy метод для обратной совместимости) */ public function validateAuthorizationData(array $data): bool { $checkHash = $data['hash'] ?? null; if (!$checkHash) { return false; } unset($data['hash']); $dataCheckArr = []; foreach ($data as $key => $value) { $dataCheckArr[] = $key . '=' . $value; } sort($dataCheckArr); $dataCheckString = implode("\n", $dataCheckArr); $secretKey = hash('sha256', $this->config->botToken, true); $hash = hash_hmac('sha256', $dataCheckString, $secretKey); if (strcmp($hash, $checkHash) !== 0) { return false; } if ((time() - ($data['auth_date'] ?? 0)) > 86400) { return false; } return true; } /** * Business Logic Layer - отправка приветственного сообщения */ private function sendWelcomeMessage(string $telegramId): void { try { $this->telegramBot->sendMessage( $telegramId, "👋 Добро пожаловать в нашу систему!\n\n" . "Вы успешно зарегистрированы. Теперь вы можете использовать наши сервисы.\n\n" . "🚀 Начните работу с нашими инструментами и получайте максимум от платформы!" ); } catch (\Exception $e) { // Не прерываем процесс авторизации если не удалось отправить сообщение Log::warning('Failed to send welcome message', [ 'telegram_id' => $telegramId, 'error' => $e->getMessage(), ]); } } /** * Получить пользователя по токену из команды /start */ public function getUserByStartToken(string $startToken): ?User { return User::query()->where('remember_token', $startToken)->firstOrFail(); } /** * Проверить валидность токена из команды /start */ public function validateStartToken(string $startToken): bool { // Здесь можно реализовать логику валидации токена // Например, проверить формат, время жизни и т.д. return !empty($startToken) && strlen($startToken) >= 6; } }