bc/public/js/auth.js
2025-05-25 17:47:38 +03:00

212 lines
13 KiB
JavaScript
Raw 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.

// /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
// на основе существующего токена или оставил его пустым.
}