emailLibrary = new EmailLibrary(); } public function register() { if ($this->request->getMethod() === 'POST') { log_message('debug', 'POST запрос получен: ' . print_r($this->request->getPost(), true)); // Валидация (упрощенная для примера) $rules = [ 'name' => 'required|min_length[3]', 'email' => 'required|valid_email|is_unique[users.email]', 'password' => 'required|min_length[6]', ]; if (!$this->validate($rules)) { return redirect()->back()->with('error', 'Ошибка регистрации'); } $userModel = new UserModel(); $orgModel = new OrganizationModel(); $orgUserModel = new OrganizationUserModel(); // Генерируем токен для подтверждения email $verificationToken = bin2hex(random_bytes(32)); // 1. Создаем пользователя с токеном верификации $userData = [ 'name' => $this->request->getPost('name'), 'email' => $this->request->getPost('email'), 'password' => $this->request->getPost('password'), // Хешируется в модели 'verification_token' => $verificationToken, 'email_verified' => 0, ]; log_message('debug', 'Registration userData: ' . print_r($userData, true)); $userId = $userModel->insert($userData); log_message('debug', 'Insert result, userId: ' . $userId); // 2. Создаем "Личную организацию" (п. 5.2.1 ТЗ) $orgData = [ 'owner_id' => $userId, 'name' => 'Личное пространство', 'type' => 'personal', ]; $orgId = $orgModel->insert($orgData); // 3. Привязываем пользователя к этой организации (роль owner) $orgUserModel->insert([ 'organization_id' => $orgId, 'user_id' => $userId, 'role' => 'owner', 'status' => 'active', 'joined_at' => date('Y-m-d H:i:s'), ]); // 4. Отправляем письмо для подтверждения email $this->emailLibrary->sendVerificationEmail( $userData['email'], $userData['name'], $verificationToken ); // 5. Показываем сообщение о необходимости подтверждения session()->setFlashdata('success', 'Регистрация успешна! Пожалуйста, проверьте вашу почту и подтвердите email.'); return redirect()->to('/register/success'); } return $this->renderTwig('auth/register'); } /** * Страница после успешной регистрации */ public function registerSuccess() { return $this->renderTwig('auth/register_success'); } /** * Подтверждение email по токену */ public function verify($token) { log_message('debug', 'Verify called with token: ' . $token); if (empty($token)) { return $this->renderTwig('auth/verify_error', [ 'message' => 'Отсутствует токен подтверждения.' ]); } $userModel = new UserModel(); // Ищем пользователя по токену $user = $userModel->where('verification_token', $token)->first(); log_message('debug', 'User found: ' . ($user ? 'yes' : 'no')); if ($user) { log_message('debug', 'User email_verified: ' . $user['email_verified']); } if (!$user) { return $this->renderTwig('auth/verify_error', [ 'message' => 'Недействительная ссылка для подтверждения. Возможно, ссылка уже была использована или истек срок её действия.' ]); } if ($user['email_verified']) { return $this->renderTwig('auth/verify_error', [ 'message' => 'Email уже подтверждён. Вы можете войти в систему.' ]); } // Подтверждаем email $updateData = [ 'email_verified' => 1, 'verified_at' => date('Y-m-d H:i:s'), 'verification_token' => null, // Удаляем токен после использования ]; $result = $userModel->update($user['id'], $updateData); log_message('debug', 'Update result: ' . ($result ? 'success' : 'failed')); log_message('debug', 'Update data: ' . print_r($updateData, true)); if (!$result) { log_message('error', 'Update errors: ' . print_r($userModel->errors(), true)); } // Отправляем приветственное письмо $this->emailLibrary->sendWelcomeEmail($user['email'], $user['name']); return $this->renderTwig('auth/verify_success', [ 'name' => $user['name'] ]); } /** * Повторная отправка письма для подтверждения */ public function resendVerification() { if ($this->request->getMethod() === 'POST') { $email = $this->request->getPost('email'); if (empty($email)) { return redirect()->back()->with('error', 'Введите email'); } $userModel = new UserModel(); $user = $userModel->where('email', $email)->first(); if (!$user) { return redirect()->back()->with('error', 'Пользователь с таким email не найден'); } if ($user['email_verified']) { return redirect()->to('/login')->with('info', 'Email уже подтверждён. Вы можете войти.'); } // Генерируем новый токен $newToken = bin2hex(random_bytes(32)); $userModel->update($user['id'], [ 'verification_token' => $newToken ]); // Отправляем письмо повторно $this->emailLibrary->sendVerificationEmail( $user['email'], $user['name'], $newToken ); return redirect()->back()->with('success', 'Письмо для подтверждения отправлено повторно. Проверьте почту.'); } return $this->renderTwig('auth/resend_verification'); } public function login() { if ($this->request->getMethod() === 'POST') { $userModel = new \App\Models\UserModel(); $orgUserModel = new \App\Models\OrganizationUserModel(); $email = $this->request->getPost('email'); $password = $this->request->getPost('password'); $user = $userModel->where('email', $email)->first(); if ($user && password_verify($password, $user['password'])) { // Проверяем, подтвержден ли email if (!$user['email_verified']) { session()->setFlashdata('warning', 'Email не подтверждён. Проверьте почту или запросите письмо повторно.'); return redirect()->to('/login'); } // Получаем организации пользователя $userOrgs = $orgUserModel->where('user_id', $user['id'])->findAll(); if (empty($userOrgs)) { // Экстремальный случай: если по какой-то причине у пользователя нет организаций session()->setFlashdata('error', 'Ваш аккаунт не привязан ни к одной организации. Обратитесь к поддержке.'); return redirect()->to('/login'); } // Базовые данные сессии (пользователь авторизован) $sessionData = [ 'user_id' => $user['id'], 'email' => $user['email'], 'name' => $user['name'], 'isLoggedIn' => true ]; // АВТОМАТИЧЕСКИЙ ВЫБОР ОРГАНИЗАЦИИ if (count($userOrgs) === 1) { // Если одна организация — заходим автоматически для удобства $sessionData['active_org_id'] = $userOrgs[0]['organization_id']; session()->set($sessionData); return redirect()->to('/'); } // ОЧИЩАЕМ active_org_id если несколько организаций session()->remove('active_org_id'); // Если несколько организаций — отправляем на страницу выбора session()->set($sessionData); // (Опционально) Записываем информацию, что пользователь залогинен, но орга не выбрана, // чтобы страница /organizations не редиректнула его обратно (см. Organizations::index) session()->setFlashdata('info', 'Выберите пространство для работы'); return redirect()->to('/organizations'); //} } else { return redirect()->back()->with('error', 'Неверный логин или пароль'); } } return $this->renderTwig('auth/login'); } public function logout() { session()->destroy(); session()->remove('active_org_id'); return redirect()->to('/'); } }