// /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 не передает API_BASE_URL, // предполагая, что fetch будет использовать относительные пути к текущему домену. // Если ваш main.js снова будет передавать API_BASE_URL, раскомментируйте и используйте его. // const API_BASE_URL = dependencies.API_BASE_URL || ''; // Пустая строка заставит fetch использовать относительные пути // Если API на другом домене, API_BASE_URL обязателен. Для относительных путей: const getApiUrl = (path) => `${window.location.origin}${path}`; // Название ключа для хранения 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(); // Отключаемся, чтобы при следующем connect отправился новый токен } // Обновляем auth объект сокета перед подключением // socket.io клиент автоматически подхватит новый токен из localStorage при следующем .connect(), // если он был инициализирован с auth: () => { token: localStorage.getItem(...) } // или мы можем явно установить его здесь: 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; // Кнопка logout управляется состоянием isLoggedIn и видимостью экрана } } // --- Обработчики событий 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; // Может быть null, если форма логина на другой странице if (regButton) regButton.disabled = true; if (loginButton) loginButton.disabled = true; ui.setAuthMessage('Регистрация...'); try { // Используем относительный путь, если API_BASE_URL не задан или пуст const response = await fetch(getApiUrl('/auth/register'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }), }); await handleAuthResponse(response, 'register'); if (response.ok && clientState.isLoggedIn && registerForm) { // Проверяем clientState.isLoggedIn для очистки registerForm.reset(); // Очищаем форму при успехе } } catch (error) { console.error('[Auth] Network error during registration:', error); ui.setAuthMessage('Ошибка сети при регистрации. Пожалуйста, проверьте ваше подключение.', true); // Разблокируем кнопки при ошибке сети, т.к. finally в handleAuthResponse может не сработать 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(getApiUrl('/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; // Блокируем кнопку сразу // Проверяем, находится ли игрок в активной игре if (clientState.isLoggedIn && clientState.isInGame && clientState.currentGameId) { // Если это PvP игра и она не закончена if (clientState.currentGameState && clientState.currentGameState.gameMode === 'pvp' && !clientState.currentGameState.isGameOver) { console.log('[Auth] Player is in an active PvP game. Emitting playerSurrender.'); socket.emit('playerSurrender'); // Не ждем ответа от сервера здесь, так как logout - это безусловное действие на клиенте. } // --- НАЧАЛО ИЗМЕНЕНИЯ ДЛЯ ВАРИАНТА А --- else if (clientState.currentGameState && clientState.currentGameState.gameMode === 'ai' && !clientState.currentGameState.isGameOver) { console.log('[Auth] Player is in an active AI game. Emitting leaveAiGame.'); socket.emit('leaveAiGame'); // Сервер должен обработать это и завершить AI игру. } // --- КОНЕЦ ИЗМЕНЕНИЯ ДЛЯ ВАРИАНТА А --- } // Серверный эндпоинт для логаута не обязателен для JWT, // если нет необходимости аннулировать токен на сервере (что сложно с JWT). // Основное действие - удаление токена на клиенте. // socket.emit('logout'); // Клиент сам инициирует разрыв и новое подключение без токена. // Это событие из bc.js скорее для очистки серверной сессии сокета. localStorage.removeItem(JWT_TOKEN_KEY); // Удаляем токен // Сбрасываем состояние клиента clientState.isLoggedIn = false; clientState.loggedInUsername = ''; clientState.myUserId = null; // clientState.isInGame и другие игровые переменные будут сброшены в ui.showAuthScreen() // или ui.resetGameVariables() если вызывается напрямую. ui.showAuthScreen(); // Показываем экран логина (это вызовет resetGameVariables) ui.setAuthMessage("Вы успешно вышли из системы."); // Сообщение на экране логина // Переподключаем сокет без токена (или он сам переподключится при следующем действии) if (socket.connected) { socket.disconnect(); // Принудительно отключаемся } socket.auth = { token: null }; // Очищаем токен в auth объекте сокета socket.connect(); // Сокет подключится как неаутентифицированный (или main.js инициирует) // Фактически, connect() будет вызван из main.js при переходе на authScreen // и проверке состояния. Здесь главное - очистить токен. // Но явный connect() после disconnect() более предсказуем. // Кнопка logoutButton.disabled = true; уже была установлена в showAuthScreen() }); } // --- Обработчики событий Socket.IO --- // Старые 'registerResponse' и 'loginResponse' (если были через сокет) больше не нужны, // так как эти ответы приходят через HTTP. // Логика проверки токена при загрузке страницы (если токен есть в localStorage) // обычно выполняется в main.js до инициализации сокета. // Здесь мы предполагаем, что main.js уже подготовил clientState // на основе существующего токена или оставил его пустым. }