add CRM module start

This commit is contained in:
root
2026-01-15 07:09:36 +03:00
parent b14f293a45
commit b810a17649
40 changed files with 3807 additions and 14 deletions
+41 -10
View File
@@ -126,6 +126,7 @@ class Auth extends BaseController
// Генерируем токен для подтверждения email
$verificationToken = bin2hex(random_bytes(32));
$tokenExpiresAt = date('Y-m-d H:i:s', strtotime('+24 hours'));
// 1. Создаем пользователя с токеном верификации
$userData = [
@@ -133,6 +134,7 @@ class Auth extends BaseController
'email' => $this->request->getPost('email'),
'password' => $this->request->getPost('password'), // Хешируется в модели
'verification_token' => $verificationToken,
'token_expires_at' => $tokenExpiresAt,
'email_verified' => 0,
];
@@ -215,6 +217,13 @@ class Auth extends BaseController
]);
}
// Проверяем срок действия токена
if (!empty($user['token_expires_at']) && strtotime($user['token_expires_at']) < time()) {
return $this->renderTwig('auth/verify_error', [
'message' => 'Ссылка для подтверждения истекла. Пожалуйста, запросите письмо повторно.'
]);
}
if ($user['email_verified']) {
return $this->renderTwig('auth/verify_error', [
'message' => 'Email уже подтверждён. Вы можете войти в систему.'
@@ -281,8 +290,10 @@ class Auth extends BaseController
// Генерируем новый токен
$newToken = bin2hex(random_bytes(32));
$newExpiresAt = date('Y-m-d H:i:s', strtotime('+24 hours'));
$userModel->update($user['id'], [
'verification_token' => $newToken
'verification_token' => $newToken,
'token_expires_at' => $newExpiresAt
]);
// Отправляем письмо повторно
@@ -347,10 +358,10 @@ class Auth extends BaseController
// === ЗАПОМНИТЬ МЕНЯ ===
$remember = $this->request->getPost('remember');
$redirectUrl = count($userOrgs) === 1 ? '/' : '/organizations';
if ($remember) {
$this->createRememberToken($user['id']);
// Устанавливаем сессию на 30 дней
$this->session->setExpiry(30 * 24 * 60 * 60); // 30 дней в секундах
$redirectUrl = $this->createRememberTokenAndRedirect($user['id'], $redirectUrl);
}
// АВТОМАТИЧЕСКИЙ ВЫБОР ОРГАНИЗАЦИИ
@@ -362,7 +373,9 @@ class Auth extends BaseController
// === СБРОС RATE LIMITING ПОСЛЕ УСПЕШНОГО ВХОДА ===
$this->resetRateLimit('login');
return redirect()->to('/');
return $redirectUrl !== '/'
? redirect()->to($redirectUrl)
: redirect()->to('/');
}
// ОЧИЩАЕМ active_org_id если несколько организаций
@@ -378,7 +391,9 @@ class Auth extends BaseController
// === СБРОС RATE LIMITING ПОСЛЕ УСПЕШНОГО ВХОДА ===
$this->resetRateLimit('login');
return redirect()->to('/organizations');
return $redirectUrl !== '/' && $redirectUrl !== '/organizations'
? redirect()->to($redirectUrl)
: redirect()->to('/organizations');
} else {
// === ЗАСЧИТЫВАЕМ НЕУДАЧНУЮ ПОПЫТКУ ===
$limitExceeded = $this->recordFailedAttempt('login');
@@ -420,8 +435,9 @@ class Auth extends BaseController
/**
* Создание remember-токена для автологина
* Возвращает массив с selector и validator
*/
protected function createRememberToken(int $userId): void
protected function createRememberTokenData(int $userId): array
{
$selector = bin2hex(random_bytes(16));
$validator = bin2hex(random_bytes(32));
@@ -439,9 +455,24 @@ class Auth extends BaseController
'ip_address' => $this->request->getIPAddress(),
]);
// Устанавливаем cookie на 30 дней
$cookie = \Config\Services::response()->setCookie('remember_selector', $selector, 30 * 24 * 60 * 60);
$cookie = \Config\Services::response()->setCookie('remember_token', $validator, 30 * 24 * 60 * 60);
return [
'selector' => $selector,
'validator' => $validator,
];
}
/**
* Создание токена и возврат редиректа с установленными куками
*/
protected function createRememberTokenAndRedirect(int $userId, string $redirectUrl)
{
$tokenData = $this->createRememberTokenData($userId);
$redirect = redirect()->to($redirectUrl);
$redirect->setCookie('remember_selector', $tokenData['selector'], 30 * 24 * 60 * 60);
$redirect->setCookie('remember_token', $tokenData['validator'], 30 * 24 * 60 * 60);
return $redirectUrl;
}
/**