bp/app/Controllers/InvitationController.php

304 lines
12 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Controllers;
use App\Models\OrganizationUserModel;
use App\Models\UserModel;
use App\Models\OrganizationModel;
use App\Services\InvitationService;
use App\Services\AccessService;
class InvitationController extends BaseController
{
protected InvitationService $invitationService;
public function __construct()
{
$this->invitationService = new InvitationService();
}
/**
* Страница принятия/отклонения приглашения
*/
public function accept(string $token)
{
$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' => 'Приглашение недействительно',
]);
}
// Получаем данные организации
$orgModel = new OrganizationModel();
$organization = $orgModel->find($invitation['organization_id']);
// Получаем данные приглашающего
$invitedByUser = null;
if ($invitation['invited_by']) {
$userModel = new UserModel();
$invitedByUser = $userModel->find($invitation['invited_by']);
}
// Определяем, авторизован ли пользователь
$currentUserId = session()->get('user_id');
$isLoggedIn = !empty($currentUserId);
// Если пользователь авторизован - проверяем, тот ли это email
$emailMatches = true;
if ($isLoggedIn && $invitation['user_id']) {
$currentUser = (new UserModel())->find($currentUserId);
// Проверяем, тот ли пользователь (по ID или email)
$emailMatches = ($currentUserId == $invitation['user_id']);
}
// Метка роли
$roleLabels = [
'owner' => 'Владелец',
'admin' => 'Администратор',
'manager' => 'Менеджер',
'guest' => 'Гость',
];
return $this->renderTwig('organizations/invitation_accept', [
'title' => 'Приглашение в ' . ($organization['name'] ?? 'организацию'),
'token' => $token,
'organization' => $organization,
'role' => $invitation['role'],
'role_label' => $roleLabels[$invitation['role']] ?? $invitation['role'],
'invited_by' => $invitedByUser,
'invited_at' => $invitation['invited_at'],
'is_logged_in' => $isLoggedIn,
'email_matches' => $emailMatches,
'current_user_id'=> $currentUserId,
]);
}
/**
* Обработка принятия приглашения
*/
public function processAccept()
{
$token = $this->request->getPost('token');
$action = $this->request->getPost('action');
if ($action === 'decline') {
return $this->decline($token);
}
$userId = session()->get('user_id');
// Если пользователь не авторизован - редирект на страницу создания пароля
if (!$userId) {
return redirect()->to('/invitation/complete/' . $token);
}
// Принимаем приглашение
$result = $this->invitationService->acceptInvitation($token, $userId);
if (!$result['success']) {
session()->setFlashdata('error', $result['message']);
return redirect()->to('/invitation/accept/' . $token);
}
session()->setFlashdata('success', 'Вы приняли приглашение в организацию "' . $result['organization_name'] . '"');
// Переключаем организацию и редиректим на главную
session()->set('active_org_id', $result['organization_id']);
(new AccessService())->resetCache();
return redirect()->to('/');
}
/**
* Отклонение приглашения
*/
public function decline(string $token)
{
$result = $this->invitationService->declineInvitation($token);
if (!$result['success']) {
session()->setFlashdata('error', $result['message']);
} else {
session()->setFlashdata('info', 'Приглашение отклонено');
}
return redirect()->to('/');
}
/**
* Страница завершения регистрации (для новых пользователей)
*/
public function complete(string $token)
{
$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' => 'Приглашение недействительно',
]);
}
// Если пользователь уже авторизован и это его приглашение
$userId = session()->get('user_id');
if ($userId && $invitation['user_id'] == $userId) {
return redirect()->to('/invitation/accept/' . $token);
}
// Получаем данные организации
$orgModel = new OrganizationModel();
$organization = $orgModel->find($invitation['organization_id']);
// Метка роли
$roleLabels = [
'owner' => 'Владелец',
'admin' => 'Администратор',
'manager' => 'Менеджер',
'guest' => 'Гость',
];
return $this->renderTwig('organizations/invitation_complete', [
'title' => 'Завершение регистрации',
'token' => $token,
'email' => $invitation['user_id'] ? '' : '', // Email возьмем из теневой записи
'organization' => $organization,
'role' => $invitation['role'],
'role_label' => $roleLabels[$invitation['role']] ?? $invitation['role'],
]);
}
/**
* Обработка завершения регистрации
*/
public function processComplete()
{
$token = $this->request->getPost('token');
$name = $this->request->getPost('name');
$password = $this->request->getPost('password');
$passwordConfirm = $this->request->getPost('password_confirm');
// Валидация
$errors = [];
if (empty($name) || strlen($name) < 2) {
$errors[] = 'Имя должно содержать минимум 2 символа';
}
if (empty($password) || strlen($password) < 8) {
$errors[] = 'Пароль должен содержать минимум 8 символов';
}
if ($password !== $passwordConfirm) {
$errors[] = 'Пароли не совпадают';
}
if (!empty($errors)) {
return redirect()->back()->withInput()->with('errors', $errors);
}
$invitation = $this->invitationService->orgUserModel->findByInviteToken($token);
if (!$invitation) {
return redirect()->to('/');
}
// Находим теневую запись пользователя
$userModel = new UserModel();
// Если user_id указан - обновляем существующего пользователя
if ($invitation['user_id']) {
$user = $userModel->find($invitation['user_id']);
if (!$user) {
session()->setFlashdata('error', 'Пользователь не найден');
return redirect()->to('/invitation/complete/' . $token);
}
// Обновляем профиль
$userModel->update($user['id'], [
'name' => $name,
'password' => $password,
]);
$userId = $user['id'];
} else {
// Создаем нового пользователя (теневой + реальный)
// На самом деле у нас уже есть запись в users - нужно её "активировать"
$shadowUsers = $userModel->where('email', $userModel->getFindByEmail($invitation['organization_id']))->findAll();
// Логика для теневых пользователей требует доработки
session()->setFlashdata('error', 'Ошибка регистрации');
return redirect()->to('/invitation/complete/' . $token);
}
// Авторизуем пользователя
$user = $userModel->find($userId);
$this->loginUser($user);
// Принимаем приглашение
$result = $this->invitationService->acceptInvitation($token, $userId);
if (!$result['success']) {
session()->setFlashdata('error', $result['message']);
return redirect()->to('/');
}
session()->setFlashdata('success', 'Добро пожаловать! Вы успешно зарегистрировались и приняли приглашение.');
// Переключаем организацию
session()->set('active_org_id', $result['organization_id']);
(new AccessService())->resetCache();
return redirect()->to('/');
}
/**
* Авторизация пользователя после регистрации
*/
protected function loginUser(array $user): void
{
session()->set([
'user_id' => $user['id'],
'email' => $user['email'],
'name' => $user['name'],
'logged_in' => true,
]);
}
}