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