add CRM module start
This commit is contained in:
+41
-10
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,7 @@ abstract class BaseController extends Controller
|
||||
|
||||
// Загружаем хелпер доступа для Twig
|
||||
helper('access');
|
||||
helper('crm_deals');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,7 +270,7 @@ abstract class BaseController extends Controller
|
||||
public function renderTwig($template, $data = [])
|
||||
{
|
||||
helper('csrf');
|
||||
|
||||
helper('crm_deals');
|
||||
$twig = \Config\Services::twig();
|
||||
|
||||
// oldInput из сессии добавляется в данные шаблона
|
||||
|
||||
@@ -25,6 +25,24 @@ class InvitationController extends BaseController
|
||||
$invitation = $this->invitationService->orgUserModel->findByInviteToken($token);
|
||||
|
||||
if (!$invitation) {
|
||||
// Проверяем, есть ли приглашение с таким токеном (может быть истекшим)
|
||||
$db = \Config\Database::connect();
|
||||
$expiredInvitation = $db->table('organization_users')
|
||||
->where('invite_token', $token)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
if ($expiredInvitation && !empty($expiredInvitation['invite_expires_at'])) {
|
||||
$expiredAt = strtotime($expiredInvitation['invite_expires_at']);
|
||||
$isExpired = $expiredAt < time();
|
||||
|
||||
return $this->renderTwig('organizations/invitation_expired', [
|
||||
'title' => $isExpired ? 'Приглашение истекло' : 'Приглашение недействительно',
|
||||
'expired' => $isExpired,
|
||||
'expired_at' => $expiredInvitation['invite_expires_at'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->renderTwig('organizations/invitation_expired', [
|
||||
'title' => 'Приглашение недействительно',
|
||||
]);
|
||||
@@ -135,6 +153,24 @@ class InvitationController extends BaseController
|
||||
$invitation = $this->invitationService->orgUserModel->findByInviteToken($token);
|
||||
|
||||
if (!$invitation) {
|
||||
// Проверяем, есть ли приглашение с таким токеном (может быть истекшим)
|
||||
$db = \Config\Database::connect();
|
||||
$expiredInvitation = $db->table('organization_users')
|
||||
->where('invite_token', $token)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
if ($expiredInvitation && !empty($expiredInvitation['invite_expires_at'])) {
|
||||
$expiredAt = strtotime($expiredInvitation['invite_expires_at']);
|
||||
$isExpired = $expiredAt < time();
|
||||
|
||||
return $this->renderTwig('organizations/invitation_expired', [
|
||||
'title' => $isExpired ? 'Приглашение истекло' : 'Приглашение недействительно',
|
||||
'expired' => $isExpired,
|
||||
'expired_at' => $expiredInvitation['invite_expires_at'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->renderTwig('organizations/invitation_expired', [
|
||||
'title' => 'Приглашение недействительно',
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user