| @copyright 2007-2026 (c) AVE.cms | @link https://ave-cms.ru | @version 3.3 */ namespace App\Frontend\Auth; defined('BASEPATH') || die('Direct access to this location is not allowed.'); use App\Frontend\Auth\Registration\RegistrationGateRegistry; use App\Frontend\Auth\Registration\CheckoutRegistrationGateInterface; use App\Common\Lifecycle; class AuthService { protected $users; protected $tokens; protected $config; public function __construct(array $config) { $this->users = new UserRepository(); $this->tokens = new TokenRepository(); $this->config = $config; } public function register(array $data) { $registering = Lifecycle::event('auth.user.registering', 'user', 'registering', null, array( 'data' => $data, ), null, array(), 'auth_service'); if ($registering->cancelled()) { throw new \RuntimeException($registering->message() !== '' ? $registering->message() : 'Регистрация отменена модулем.'); } $data = $registering->value('data', $data); $gate = RegistrationGateRegistry::get($this->setting('registration_gate', 'email')); if (!$gate) { throw new \RuntimeException('Канал регистрации не настроен.'); } $email = $gate->normalize(isset($data['email']) ? $data['email'] : ''); if (!$gate->valid($email)) { throw new \InvalidArgumentException('Укажите корректный email.'); } if ($this->deniedEmail($email)) { throw new \InvalidArgumentException('Регистрация с этим email недоступна.'); } if ($this->users->findByEmail($email)) { throw new \InvalidArgumentException('Пользователь с таким email уже зарегистрирован.'); } $data['email'] = $email; $user = $this->users->create($data, $this->defaultGroup()); $extra = isset($data['extra']) && is_array($data['extra']) ? $data['extra'] : array(); (new ProfileRepository())->save((int) $user['Id'], $extra, 'registration'); $mode = $this->setting('registration_mode', 'email'); if ($mode === 'now') { $this->users->activate((int) $user['Id']); } elseif ($mode === 'email') { $token = $this->tokens->issue((int) $user['Id'], 'registration', $gate->code(), $this->setting('verification_ttl', 86400)); $gate->send($user, $token); } $registered = Lifecycle::event('auth.user.registered', 'user', 'registered', isset($user['Id']) ? (int) $user['Id'] : 0, array( 'user' => $user, 'gate' => $gate->code(), 'mode' => $mode, ), $user, array(), 'auth_service'); return is_array($registered->result()) ? $registered->result() : $user; } public function verifyRegistration($token) { $row = $this->tokens->consume($token, 'registration'); if (!$row) { return false; } $this->users->verifyEmail((int) $row['user_id']); return true; } public function checkoutOptions() { $gate = $this->checkoutGate(); if (!$gate || empty($this->config['registration_enabled'])) { return array('enabled' => false, 'policy_url' => '', 'policy_label' => ''); } $options = $gate->checkoutOptions(); return array( 'enabled' => !empty($options['enabled']) && !empty($this->config['password_reset_enabled']), 'policy_url' => isset($options['policy_url']) ? (string) $options['policy_url'] : '', 'policy_label' => isset($options['policy_label']) ? (string) $options['policy_label'] : '', ); } public function checkoutAccountState($email) { $gate = $this->checkoutGate(); $email = $gate ? $gate->normalize($email) : mb_strtolower(trim((string) $email)); if (!$gate || !$gate->valid($email)) { return array('valid' => false, 'exists' => false, 'email' => $email); } return array('valid' => true, 'exists' => $this->users->findByEmail($email) !== null, 'email' => $email); } public function registerCheckout(array $data, array $context = array()) { $gate = $this->checkoutGate(); $options = $this->checkoutOptions(); if (!$gate || empty($options['enabled'])) { throw new \RuntimeException('Регистрация при оформлении заказа отключена.'); } if (empty($context['consent'])) { throw new \InvalidArgumentException('Подтвердите согласие с политикой конфиденциальности.'); } $email = $gate->normalize(isset($data['email']) ? $data['email'] : ''); if (!$gate->valid($email)) { throw new \InvalidArgumentException('Для регистрации укажите корректный email.'); } if ($this->deniedEmail($email)) { throw new \InvalidArgumentException('Регистрация с этим email недоступна.'); } if ($this->users->findByEmail($email)) { throw new \InvalidArgumentException('Пользователь с таким email уже зарегистрирован. Войдите в аккаунт.'); } $data['email'] = $email; $registering = Lifecycle::event('auth.user.registering', 'user', 'registering', null, array( 'data' => $data, 'gate' => $gate->code(), 'mode' => 'checkout', 'context' => $context, ), null, array(), 'auth_service'); if ($registering->cancelled()) { throw new \RuntimeException($registering->message() !== '' ? $registering->message() : 'Регистрация отменена модулем.'); } $data = $registering->value('data', $data); $user = $this->users->createCheckout($data, $this->defaultGroup()); $registered = Lifecycle::event('auth.user.registered', 'user', 'registered', (int) $user['Id'], array( 'user' => $user, 'gate' => $gate->code(), 'mode' => 'checkout', 'context' => $context, ), $user, array(), 'auth_service'); return is_array($registered->result()) ? $registered->result() : $user; } public function sendCheckoutAccess(array $user, array $context = array()) { $gate = $this->checkoutGate(); if (!$gate) { return false; } $ttl = max(300, (int) $this->setting('reset_ttl', 3600)); $token = $this->tokens->issue((int) $user['Id'], 'checkout_access', $gate->code(), $ttl); $context['ttl'] = $ttl; $gate->sendCheckoutAccess($user, $token, $context); return true; } public function requestPasswordReset($email) { $user = $this->users->findByEmail($email); if (!$user || (string) $user['status'] !== '1') { return; } $raw = $this->tokens->issue((int) $user['Id'], 'password_reset', 'email', $this->setting('reset_ttl', 3600)); $url = rtrim(defined('HOST') ? HOST : '', '/') . Feature::url('reset') . '?token=' . rawurlencode($raw); $html = '