// /public/js/auth.js // Эта функция будет вызвана из main.js и получит необходимые зависимости export function initAuth(dependencies) { const { socket, clientState, ui } = dependencies; const { loginForm, registerForm, logoutButton } = ui.elements; // Получаем нужные DOM элементы // URL вашего API сервера. Лучше вынести в конфигурацию или передавать. // Для примера захардкодим, но в main.js можно будет это улучшить. const API_BASE_URL = dependencies.API_BASE_URL || 'http://127.0.0.1:3200'; // Убедитесь, что это ваш URL // Название ключа для хранения JWT в localStorage const JWT_TOKEN_KEY = 'jwtToken'; async function handleAuthResponse(response, formType) { const regButton = registerForm ? registerForm.querySelector('button') : null; const loginButton = loginForm ? loginForm.querySelector('button') : null; try { const data = await response.json(); if (response.ok && data.success && data.token) { // Успешная аутентификация/регистрация localStorage.setItem(JWT_TOKEN_KEY, data.token); // Сохраняем токен clientState.isLoggedIn = true; clientState.loggedInUsername = data.username; clientState.myUserId = data.userId; ui.setAuthMessage(''); // Очищаем сообщение об аутентификации ui.showGameSelectionScreen(data.username); // Показываем экран выбора игры // Важно: переподключить сокет с новым токеном if (socket.connected) { socket.disconnect(); } // Обновляем auth объект сокета перед подключением // В main.js при создании сокета, он должен уже брать токен из localStorage // Но если сокет уже существует, нужно обновить его auth данные socket.auth = { token: data.token }; socket.connect(); // Это вызовет 'connect' и 'requestGameState' в main.js } else { // Ошибка аутентификации/регистрации clientState.isLoggedIn = false; clientState.loggedInUsername = ''; clientState.myUserId = null; localStorage.removeItem(JWT_TOKEN_KEY); // Удаляем старый токен, если был ui.setAuthMessage(data.message || 'Ошибка сервера.', true); } } catch (error) { // Ошибка парсинга JSON или другая сетевая ошибка console.error(`[Auth] Error processing ${formType} response:`, error); clientState.isLoggedIn = false; clientState.loggedInUsername = ''; clientState.myUserId = null; localStorage.removeItem(JWT_TOKEN_KEY); ui.setAuthMessage('Произошла ошибка сети. Попробуйте снова.', true); } finally { // Разблокируем кнопки в любом случае if (regButton) regButton.disabled = false; if (loginButton) loginButton.disabled = false; if (logoutButton && clientState.isLoggedIn) logoutButton.disabled = false; } } // --- Обработчики событий DOM --- if (registerForm) { registerForm.addEventListener('submit', async (e) => { e.preventDefault(); const usernameInput = document.getElementById('register-username'); const passwordInput = document.getElementById('register-password'); if (!usernameInput || !passwordInput) return; const username = usernameInput.value; const password = passwordInput.value; const regButton = registerForm.querySelector('button'); const loginButton = loginForm ? loginForm.querySelector('button') : null; if (regButton) regButton.disabled = true; if (loginButton) loginButton.disabled = true; ui.setAuthMessage('Регистрация...'); try { const response = await fetch(`${API_BASE_URL}/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }), }); await handleAuthResponse(response, 'register'); if (response.ok && registerForm) registerForm.reset(); // Очищаем форму при успехе } catch (error) { console.error('[Auth] Network error during registration:', error); ui.setAuthMessage('Ошибка сети при регистрации. Пожалуйста, проверьте ваше подключение.', true); if (regButton) regButton.disabled = false; if (loginButton) loginButton.disabled = false; } }); } if (loginForm) { loginForm.addEventListener('submit', async (e) => { e.preventDefault(); const usernameInput = document.getElementById('login-username'); const passwordInput = document.getElementById('login-password'); if (!usernameInput || !passwordInput) return; const username = usernameInput.value; const password = passwordInput.value; const loginButton = loginForm.querySelector('button'); const regButton = registerForm ? registerForm.querySelector('button') : null; if (loginButton) loginButton.disabled = true; if (regButton) regButton.disabled = true; ui.setAuthMessage('Вход...'); try { const response = await fetch(`${API_BASE_URL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }), }); await handleAuthResponse(response, 'login'); } catch (error) { console.error('[Auth] Network error during login:', error); ui.setAuthMessage('Ошибка сети при входе. Пожалуйста, проверьте ваше подключение.', true); if (loginButton) loginButton.disabled = false; if (regButton) regButton.disabled = false; } }); } if (logoutButton) { logoutButton.addEventListener('click', () => { logoutButton.disabled = true; // --- НАЧАЛО ИЗМЕНЕНИЯ --- // Если игрок в активной PvP игре, отправляем сигнал о сдаче if (clientState.isLoggedIn && clientState.isInGame && clientState.currentGameId && clientState.currentGameState && // Убедимся, что gameState существует clientState.currentGameState.gameMode === 'pvp' && // Проверяем режим игры !clientState.currentGameState.isGameOver) { // Только если игра еще не закончена console.log('[Auth] Player is in an active PvP game. Emitting playerSurrender.'); socket.emit('playerSurrender'); // Не ждем ответа от сервера здесь, так как logout - это безусловное действие на клиенте. // Сервер обработает 'playerSurrender' и соответствующим образом завершит игру. } // --- КОНЕЦ ИЗМЕНЕНИЯ --- // Серверный эндпоинт для логаута не обязателен для JWT, // если нет необходимости аннулировать токен на сервере (что сложно с JWT). // Основное действие - удаление токена на клиенте. // socket.emit('logout'); // Можно оставить, если на сервере есть логика для этого (например, GameManager.handleDisconnect) localStorage.removeItem(JWT_TOKEN_KEY); // Удаляем токен clientState.isLoggedIn = false; clientState.loggedInUsername = ''; clientState.myUserId = null; // isInGame и другие игровые переменные сбросятся в ui.showAuthScreen() // ui.disableGameControls() также будет вызван опосредованно ui.showAuthScreen(); // Показываем экран логина ui.setGameStatusMessage("Вы вышли из системы."); // Можно заменить на ui.setAuthMessage, если хотим видеть сообщение на экране логина // Переподключаем сокет без токена if (socket.connected) { socket.disconnect(); } socket.auth = { token: null }; // Очищаем токен в auth объекте сокета socket.connect(); // Сокет подключится как неаутентифицированный // Кнопка logout будет активирована, когда пользователь снова войдет // или если она видна только залогиненным пользователям, то исчезнет. // (В showAuthScreen logoutButton.disabled устанавливается в true) }); } // --- Обработчики событий Socket.IO --- // Старые 'registerResponse' и 'loginResponse' больше не нужны, // так как эти ответы приходят через HTTP. // Можно добавить обработчик для принудительного разлогинивания от сервера, если такой будет // socket.on('forceLogout', (data) => { // console.log('[Auth] Forced logout by server:', data.message); // localStorage.removeItem(JWT_TOKEN_KEY); // clientState.isLoggedIn = false; // clientState.loggedInUsername = ''; // clientState.myUserId = null; // ui.showAuthScreen(); // ui.setAuthMessage(data.message || "Вы были разлогинены сервером."); // if (socket.connected) socket.disconnect(); // socket.auth = { token: null }; // socket.connect(); // }); // При загрузке модуля auth.js, проверяем, нет ли уже токена в localStorage // Эта логика лучше всего будет работать в main.js при инициализации сокета, // но здесь можно было бы сделать предварительную проверку и обновление clientState, // если бы это было необходимо до создания сокета. // Однако, поскольку сокет создается в main.js и сразу использует токен из localStorage, // отдельная логика здесь не так критична. }