547 lines
18 KiB
JavaScript
547 lines
18 KiB
JavaScript
/**
|
||
* =============================================================================
|
||
* 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();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Обновить отображение пользователя
|
||
*/
|
||
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');
|
||
|
||
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';
|
||
} else {
|
||
badgeEl.style.display = 'none';
|
||
if (adminBtn) adminBtn.style.display = 'none';
|
||
if (serverUrlGroup) serverUrlGroup.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') return;
|
||
|
||
try {
|
||
const response = await fetch('/api/settings/admin', {
|
||
headers: { 'Authorization': `Bearer ${authToken}` }
|
||
});
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
const s = data.settings;
|
||
|
||
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 || '';
|
||
}
|
||
} 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
|
||
};
|
||
|
||
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');
|
||
}
|
||
} catch (error) {
|
||
showNotification('Ошибка сохранения', '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 = '<tr><td colspan="4" style="text-align: center; color: var(--text-muted);">Логи не найдены</td></tr>';
|
||
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 = `
|
||
<td>${time}</td>
|
||
<td>${log.username || 'Гость'}</td>
|
||
<td><span class="log-action-badge ${log.actionType}">${log.actionType}</span></td>
|
||
<td title="${JSON.stringify(log.actionData)}">${details}</td>
|
||
`;
|
||
|
||
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;
|