/** * ============================================================================= * Texas Hold'em - Модуль авторизации (клиент) * ============================================================================= */ // Текущий пользователь let currentUser = null; let authToken = null; let isGuest = false; // Пагинация логов let currentLogPage = 1; const logsPerPage = 20; // ============================================================================= // АВТОРИЗАЦИЯ // ============================================================================= /** * Инициализация авторизации */ async function initAuth() { // Проверяем сохранённый токен const savedToken = localStorage.getItem('authToken'); if (savedToken) { try { const response = await fetch('/api/auth/me', { headers: { 'Authorization': `Bearer ${savedToken}` } }); if (response.ok) { const data = await response.json(); currentUser = data.user; authToken = savedToken; onAuthSuccess(); return; } } catch (error) { console.error('Ошибка проверки токена:', error); } // Токен недействителен localStorage.removeItem('authToken'); } // Показываем экран авторизации showScreen('auth-screen'); } /** * Переключить вкладку авторизации */ function switchAuthTab(tab) { const tabs = document.querySelectorAll('#auth-tabs .btn-tab'); tabs.forEach(t => t.classList.remove('active')); event.target.classList.add('active'); document.getElementById('login-form').style.display = tab === 'login' ? 'block' : 'none'; document.getElementById('register-form').style.display = tab === 'register' ? 'block' : 'none'; } /** * Обработчик входа */ async function handleLogin() { const username = document.getElementById('login-username').value.trim(); const password = document.getElementById('login-password').value; if (!username || !password) { showNotification('Введите логин и пароль', 'error'); return; } try { const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const data = await response.json(); if (response.ok) { currentUser = data.user; authToken = data.token; localStorage.setItem('authToken', authToken); isGuest = false; onAuthSuccess(); } else { showNotification(data.error || 'Ошибка входа', 'error'); } } catch (error) { console.error('Ошибка входа:', error); showNotification('Ошибка соединения с сервером', 'error'); } } /** * Обработчик регистрации */ async function handleRegister() { const username = document.getElementById('register-username').value.trim(); const password = document.getElementById('register-password').value; const passwordConfirm = document.getElementById('register-password-confirm').value; if (!username || !password) { showNotification('Заполните все поля', 'error'); return; } if (password !== passwordConfirm) { showNotification('Пароли не совпадают', 'error'); return; } if (username.length < 3) { showNotification('Логин должен быть минимум 3 символа', 'error'); return; } if (password.length < 6) { showNotification('Пароль должен быть минимум 6 символов', 'error'); return; } try { const response = await fetch('/api/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const data = await response.json(); if (response.ok) { currentUser = data.user; authToken = data.token; localStorage.setItem('authToken', authToken); isGuest = false; showNotification('Регистрация успешна!', 'success'); onAuthSuccess(); } else { showNotification(data.error || 'Ошибка регистрации', 'error'); } } catch (error) { console.error('Ошибка регистрации:', error); showNotification('Ошибка соединения с сервером', 'error'); } } /** * Играть как гость */ function playAsGuest() { currentUser = { id: 'guest_' + Date.now(), username: 'Гость_' + Math.floor(Math.random() * 10000), role: 'guest' }; isGuest = true; authToken = null; onAuthSuccess(); } /** * Выход */ function handleLogout() { currentUser = null; authToken = null; isGuest = false; localStorage.removeItem('authToken'); showScreen('auth-screen'); showNotification('Вы вышли из системы', 'info'); } /** * После успешной авторизации */ function onAuthSuccess() { showScreen('main-menu'); updateUserDisplay(); loadLLMStatus(); // Загружаем настройки с сервера если не гость if (!isGuest && authToken) { loadUserSettingsFromServer(); } // Проверяем наличие комнаты в URL для автоматического присоединения if (typeof checkRoomInUrl === 'function') { checkRoomInUrl(); } } /** * Обновить отображение пользователя */ function updateUserDisplay() { const nameEl = document.getElementById('user-display-name'); const badgeEl = document.getElementById('user-role-badge'); const adminBtn = document.getElementById('admin-btn'); const serverUrlGroup = document.getElementById('server-url-group'); const adminLlmButtonGroup = document.getElementById('admin-llm-button-group'); if (currentUser) { nameEl.textContent = currentUser.username; if (currentUser.role === 'admin') { badgeEl.style.display = 'inline'; badgeEl.textContent = 'ADMIN'; if (adminBtn) adminBtn.style.display = 'block'; if (serverUrlGroup) serverUrlGroup.style.display = 'block'; if (adminLlmButtonGroup) adminLlmButtonGroup.style.display = 'block'; } else { badgeEl.style.display = 'none'; if (adminBtn) adminBtn.style.display = 'none'; if (serverUrlGroup) serverUrlGroup.style.display = 'none'; if (adminLlmButtonGroup) adminLlmButtonGroup.style.display = 'none'; } // Обновляем поля имени в формах document.getElementById('sp-player-name').value = currentUser.username; document.getElementById('mp-player-name').value = currentUser.username; } } // ============================================================================= // НАСТРОЙКИ С СЕРВЕРА // ============================================================================= /** * Загрузить пользовательские настройки с сервера */ async function loadUserSettingsFromServer() { if (!authToken) return; try { const response = await fetch('/api/settings/user', { headers: { 'Authorization': `Bearer ${authToken}` } }); if (response.ok) { const data = await response.json(); applyServerSettings(data.settings); } } catch (error) { console.error('Ошибка загрузки настроек:', error); } } /** * Сохранить настройки на сервер */ async function saveUserSettingsToServer(settings) { if (!authToken) return; try { await fetch('/api/settings/user', { method: 'POST', headers: { 'Authorization': `Bearer ${authToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify(settings) }); } catch (error) { console.error('Ошибка сохранения настроек:', error); } } /** * Применить настройки с сервера */ function applyServerSettings(serverSettings) { settings = { ...settings, sound: serverSettings.sound, animations: serverSettings.animations, showHandStrength: serverSettings.showHandStrength, autofold: serverSettings.autofold, cardBackStyle: serverSettings.cardBackStyle, cardBackCustom: serverSettings.cardBackCustom }; // Применяем к UI document.getElementById('setting-sound').checked = settings.sound !== false; document.getElementById('setting-animations').checked = settings.animations !== false; document.getElementById('setting-hand-strength').checked = settings.showHandStrength !== false; document.getElementById('setting-autofold').checked = settings.autofold !== false; soundEnabled = settings.sound !== false; // Применяем рубашку карт if (serverSettings.cardBackCustom) { localStorage.setItem('customCardBack', serverSettings.cardBackCustom); } applyCardBackStyle(serverSettings.cardBackStyle || 'default'); } /** * Загрузить статус LLM */ async function loadLLMStatus() { try { const response = await fetch('/api/settings/public'); const data = await response.json(); const statusBox = document.getElementById('llm-status-box'); const statusIcon = document.getElementById('llm-status-icon'); const statusText = document.getElementById('llm-status-text'); if (statusBox) { if (data.llmEnabled) { statusBox.classList.add('enabled'); statusBox.classList.remove('disabled'); statusIcon.textContent = '✅'; statusText.textContent = `LLM чат включён (${data.llmProvider})`; // Сохраняем настройки LLM глобально window.llmSettings = { enabled: true, provider: data.llmProvider, apiUrl: data.llmApiUrl, model: data.llmModel }; } else { statusBox.classList.add('disabled'); statusBox.classList.remove('enabled'); statusIcon.textContent = '❌'; statusText.textContent = 'LLM чат отключён администратором'; window.llmSettings = { enabled: false }; } } } catch (error) { console.error('Ошибка загрузки статуса LLM:', error); } } // ============================================================================= // АДМИН-ПАНЕЛЬ // ============================================================================= /** * Переключить вкладку админки */ function switchAdminTab(tab) { const tabs = document.querySelectorAll('.admin-tabs .btn-tab'); tabs.forEach(t => t.classList.remove('active')); event.target.classList.add('active'); document.querySelectorAll('.admin-tab-content').forEach(el => { el.style.display = 'none'; }); document.getElementById(`admin-tab-${tab}`).style.display = 'block'; // Загружаем данные вкладки if (tab === 'logs') { loadLogStats(); loadLogs(1); } else if (tab === 'settings') { loadAdminSettings(); } } /** * Загрузить админские настройки */ async function loadAdminSettings() { if (!authToken || currentUser?.role !== 'admin') { console.log('❌ Нет прав для загрузки админ настроек'); return; } console.log('📥 Загрузка админских настроек...'); try { const response = await fetch('/api/settings/admin', { headers: { 'Authorization': `Bearer ${authToken}` } }); if (response.ok) { const data = await response.json(); const s = data.settings; console.log('✅ Получены настройки:', s); document.getElementById('admin-llm-enabled').checked = s.llmEnabled; document.getElementById('admin-llm-provider').value = s.llmProvider || 'ollama'; document.getElementById('admin-llm-url').value = s.llmApiUrl || 'http://localhost:11434'; document.getElementById('admin-llm-model').value = s.llmModel || 'llama3.2'; document.getElementById('admin-llm-apikey').value = s.llmApiKey || ''; } else { console.error('❌ Ошибка ответа сервера:', response.status); } } catch (error) { console.error('❌ Ошибка загрузки админ настроек:', error); } } /** * Сохранить админские настройки */ async function saveAdminSettings() { if (!authToken || currentUser?.role !== 'admin') return; const settings = { llmEnabled: document.getElementById('admin-llm-enabled').checked, llmProvider: document.getElementById('admin-llm-provider').value, llmApiUrl: document.getElementById('admin-llm-url').value, llmModel: document.getElementById('admin-llm-model').value, llmApiKey: document.getElementById('admin-llm-apikey').value }; console.log('💾 Сохранение админских настроек:', settings); try { const response = await fetch('/api/settings/admin', { method: 'POST', headers: { 'Authorization': `Bearer ${authToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify(settings) }); if (response.ok) { showNotification('Настройки сохранены', 'success'); // Обновляем статус LLM для отображения в настройках loadLLMStatus(); } else { showNotification('Ошибка сохранения', 'error'); } } catch (error) { showNotification('Ошибка сохранения', 'error'); console.error('Ошибка:', error); } } /** * Тестировать LLM из админки */ async function testAdminLLM() { // Сохраняем настройки сначала await saveAdminSettings(); showNotification('Тестирование LLM...', 'info'); // Здесь можно добавить реальный тест через сервер setTimeout(() => { showNotification('LLM тест выполнен (проверьте логи сервера)', 'success'); }, 1000); } /** * Загрузить статистику логов */ async function loadLogStats() { if (!authToken || currentUser?.role !== 'admin') return; try { const response = await fetch('/api/logs/stats', { headers: { 'Authorization': `Bearer ${authToken}` } }); if (response.ok) { const data = await response.json(); document.getElementById('stat-total').textContent = data.stats.total; document.getElementById('stat-today').textContent = data.stats.today; } } catch (error) { console.error('Ошибка загрузки статистики:', error); } } /** * Загрузить логи */ async function loadLogs(page = 1) { if (!authToken || currentUser?.role !== 'admin') return; currentLogPage = Math.max(1, page); const offset = (currentLogPage - 1) * logsPerPage; const actionType = document.getElementById('log-filter-type').value; try { let url = `/api/logs?limit=${logsPerPage}&offset=${offset}`; if (actionType) url += `&actionType=${actionType}`; const response = await fetch(url, { headers: { 'Authorization': `Bearer ${authToken}` } }); if (response.ok) { const data = await response.json(); renderLogs(data.logs); // Обновляем пагинацию document.getElementById('log-page-info').textContent = `Страница ${currentLogPage}`; document.getElementById('log-prev-btn').disabled = currentLogPage === 1; document.getElementById('log-next-btn').disabled = data.logs.length < logsPerPage; } } catch (error) { console.error('Ошибка загрузки логов:', error); } } /** * Отрисовать логи */ function renderLogs(logs) { const tbody = document.getElementById('log-table-body'); tbody.innerHTML = ''; if (logs.length === 0) { tbody.innerHTML = 'Логи не найдены'; return; } logs.forEach(log => { const tr = document.createElement('tr'); const time = new Date(log.timestamp).toLocaleString('ru-RU'); const details = typeof log.actionData === 'object' ? JSON.stringify(log.actionData).substring(0, 50) + '...' : log.actionData; tr.innerHTML = ` ${time} ${log.username || 'Гость'} ${log.actionType} ${details} `; tbody.appendChild(tr); }); } // ============================================================================= // ОЧИСТКА ЧАТА // ============================================================================= /** * Очистить чат при смене режима игры */ function clearGameChat() { const container = document.getElementById('game-chat-messages'); if (container) { container.innerHTML = ''; } } /** * Очистить чат лобби */ function clearLobbyChat() { const container = document.getElementById('lobby-chat-messages'); if (container) { container.innerHTML = ''; } } // Экспортируем для глобального использования window.currentUser = currentUser; window.authToken = authToken; window.isGuest = isGuest;