From ec48a14053dd501700eb62372824425b8df1bcfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9=20=D0=A1?= =?UTF-8?q?=D0=B0=D1=84=D1=80=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Wed, 29 Apr 2026 21:44:21 +0000 Subject: [PATCH] Delete internal/api/static/app.js --- internal/api/static/app.js | 959 ------------------------------------- 1 file changed, 959 deletions(-) delete mode 100644 internal/api/static/app.js diff --git a/internal/api/static/app.js b/internal/api/static/app.js deleted file mode 100644 index e7cf296..0000000 --- a/internal/api/static/app.js +++ /dev/null @@ -1,959 +0,0 @@ -/* - * Copyright 2026 Safronov Grigorii - * - * Licensed under the CDDL, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * https://opensource.org/licenses/CDDL-1.0 - */ - -// Файл: internal/api/static/app.js -// JavaScript для веб-интерфейса Futriis DB Dashboard - -// Глобальное состояние -let currentSession = null; -let currentDatabase = null; -let currentCollection = null; -let currentUser = null; - -// DOM элементы -const contentArea = document.getElementById('contentArea'); -const pageTitle = document.getElementById('pageTitle'); -const connectionStatus = document.getElementById('connectionStatus'); -const userInfoSpan = document.querySelector('#userInfo span'); -const logoutBtn = document.getElementById('logoutBtn'); -const menuToggle = document.getElementById('menuToggle'); -const sidebar = document.querySelector('.sidebar'); -const modal = document.getElementById('modal'); -const modalTitle = document.getElementById('modalTitle'); -const modalBody = document.getElementById('modalBody'); -const modalConfirm = document.getElementById('modalConfirm'); -const modalCloseBtns = document.querySelectorAll('.modal-close'); - -// Инициализация приложения -document.addEventListener('DOMContentLoaded', () => { - checkSession(); - initNavigation(); - initEventListeners(); -}); - -// Проверка сессии -async function checkSession() { - try { - const response = await fetch('/api/webui/session'); - const data = await response.json(); - - if (data.success && data.data.authenticated) { - currentUser = data.data.username; - userInfoSpan.textContent = currentUser; - - if (data.data.connection_status === 'connected') { - connectionStatus.className = 'connection-status online'; - connectionStatus.innerHTML = 'СУБД подключена'; - } else { - connectionStatus.className = 'connection-status offline'; - connectionStatus.innerHTML = 'СУБД не подключена'; - } - - loadDashboard(); - } else { - showLoginModal(); - } - } catch (error) { - console.error('Session check failed:', error); - showLoginModal(); - } -} - -// Функция для проверки статуса подключения -function startConnectionStatusMonitor() { - setInterval(async () => { - if (currentUser) { - try { - const response = await fetch('/api/webui/session'); - const data = await response.json(); - - if (data.success && data.data.connection_status === 'connected') { - connectionStatus.className = 'connection-status online'; - connectionStatus.innerHTML = 'СУБД подключена'; - } else { - connectionStatus.className = 'connection-status offline'; - connectionStatus.innerHTML = 'СУБД не подключена'; - } - } catch (error) { - connectionStatus.className = 'connection-status offline'; - connectionStatus.innerHTML = 'СУБД не подключена'; - } - } - }, 5000); -} - -// Показать модальное окно входа -function showLoginModal() { - modalTitle.textContent = 'Вход в систему субд Futriis'; - modalBody.innerHTML = ` -
- - -
-
- - -
- `; - - // Меняем текст на кнопке "Подтвердить" на "Войти" - modalConfirm.textContent = 'Войти'; - - modal.classList.add('show'); - - const confirmHandler = async () => { - const username = document.getElementById('username').value; - const password = document.getElementById('password').value; - - if (!username || !password) { - showNotification('Пожалуйста, заполните все поля', 'error'); - return; - } - - try { - const response = await fetch('/api/webui/login', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ username, password }) - }); - - const data = await response.json(); - - if (data.success) { - currentUser = username; - userInfoSpan.textContent = username; - modal.classList.remove('show'); - showNotification('Вход выполнен успешно', 'success'); - connectionStatus.className = 'connection-status online'; - connectionStatus.innerHTML = 'СУБД подключена'; - startConnectionStatusMonitor(); - loadDashboard(); - } else { - showNotification(data.error || 'Неверный логин и/или пароль', 'error'); - } - } catch (error) { - showNotification('Ошибка подключения к серверу', 'error'); - } - }; - - modalConfirm.onclick = confirmHandler; - - // Обработка Enter - const handleEnter = (e) => { - if (e.key === 'Enter') { - confirmHandler(); - document.removeEventListener('keydown', handleEnter); - } - }; - document.addEventListener('keydown', handleEnter); -} - -// Инициализация навигации -function initNavigation() { - document.querySelectorAll('.nav-link[data-section]').forEach(link => { - link.addEventListener('click', (e) => { - e.preventDefault(); - const section = link.dataset.section; - loadSection(section); - setActiveNav(link); - }); - }); - - document.querySelectorAll('[data-action]').forEach(item => { - item.addEventListener('click', (e) => { - e.preventDefault(); - const action = item.dataset.action; - handleCrudAction(action); - }); - }); - - document.querySelectorAll('.has-submenu > .nav-link').forEach(link => { - link.addEventListener('click', (e) => { - e.preventDefault(); - const parent = link.closest('.has-submenu'); - parent.classList.toggle('open'); - }); - }); -} - -// Инициализация обработчиков событий -function initEventListeners() { - logoutBtn.addEventListener('click', async () => { - await fetch('/api/webui/logout', { method: 'POST' }); - currentSession = null; - currentUser = null; - connectionStatus.className = 'connection-status offline'; - connectionStatus.innerHTML = 'СУБД не подключена'; - showLoginModal(); - }); - - if (menuToggle) { - menuToggle.addEventListener('click', () => { - sidebar.classList.toggle('open'); - }); - } - - modalCloseBtns.forEach(btn => { - btn.addEventListener('click', () => { - modal.classList.remove('show'); - }); - }); - - modal.addEventListener('click', (e) => { - if (e.target === modal) { - modal.classList.remove('show'); - } - }); -} - -// Загрузка секции -async function loadSection(section) { - switch(section) { - case 'dashboard': - loadDashboard(); - break; - case 'cluster': - loadClusterManagement(); - break; - case 'audit': - loadAuditLog(); - break; - case 'settings': - loadSettings(); - break; - default: - loadDashboard(); - } -} - -// Загрузка дашборда -async function loadDashboard() { - pageTitle.textContent = 'Панель управления'; - contentArea.innerHTML = '

Загрузка данных...

'; - - try { - const [statsRes, dbsRes] = await Promise.all([ - fetch('/api/webui/stats'), - fetch('/api/webui/databases') - ]); - - const stats = await statsRes.json(); - const databases = await dbsRes.json(); - - contentArea.innerHTML = ` -
-
-
-
-

${stats.data.databases || 0}

-

Базы данных

-
-
-
-
-
-

${stats.data.collections || 0}

-

Коллекции

-
-
-
-
-
-

${stats.data.documents || 0}

-

Документы

-
-
-
-
-
-

${stats.data.storage_used_mb?.toFixed(2) || 0} MB

-

Использовано памяти

-
-
-
- -
-

Базы данных

- - - - - - ${databases.data.map(db => ` - - - - - - `).join('')} - -
Имя БДКоллекцииДействия
${escapeHtml(db.name)}${db.collections} - -
-
- `; - } catch (error) { - contentArea.innerHTML = '
Ошибка загрузки данных
'; - showNotification('Ошибка загрузки дашборда', 'error'); - } -} - -// Просмотр базы данных -window.viewDatabase = async function(dbName) { - currentDatabase = dbName; - pageTitle.textContent = `База данных: ${dbName}`; - contentArea.innerHTML = '

Загрузка коллекций...

'; - - try { - const response = await fetch(`/api/webui/collections/${dbName}`); - const data = await response.json(); - - if (data.success) { - contentArea.innerHTML = ` -
-
-

Коллекции

- -
- - - - - - ${data.data.collections.map(coll => ` - - - - - - - - `).join('')} - -
Имя коллекцииДокументовРазмерИндексыДействия
${escapeHtml(coll.name)}${coll.count}${(coll.size / 1024).toFixed(2)} KB${coll.indexes.length} - - -
-
- `; - } else { - contentArea.innerHTML = '
Ошибка загрузки коллекций
'; - } - } catch (error) { - contentArea.innerHTML = '
Ошибка подключения
'; - } -}; - -// Просмотр коллекции -window.viewCollection = async function(dbName, collName) { - currentDatabase = dbName; - currentCollection = collName; - pageTitle.textContent = `Коллекция: ${dbName}.${collName}`; - contentArea.innerHTML = '

Загрузка документов...

'; - - try { - const response = await fetch(`/api/webui/documents/${dbName}/${collName}?limit=100`); - const data = await response.json(); - - if (data.success) { - contentArea.innerHTML = ` -
- - -
- -
-

Документы (${data.data.total} всего)

- - - - - - ${data.data.documents.map(doc => ` - - - - - - - `).join('')} - -
IDПоляСозданДействия
${escapeHtml(doc.id)}
${escapeHtml(JSON.stringify(doc.fields, null, 2))}
${new Date(doc.created_at).toLocaleString()} - - -
-
- `; - } else { - contentArea.innerHTML = '
Ошибка загрузки документов
'; - } - } catch (error) { - contentArea.innerHTML = '
Ошибка подключения
'; - } -}; - -// Загрузка управления кластером -async function loadClusterManagement() { - pageTitle.textContent = 'Управление кластером'; - contentArea.innerHTML = '

Загрузка информации о кластере...

'; - - try { - const [statusRes, nodesRes] = await Promise.all([ - fetch('/api/webui/cluster/status'), - fetch('/api/webui/cluster/nodes') - ]); - - const status = await statusRes.json(); - const nodes = await nodesRes.json(); - - contentArea.innerHTML = ` -
-
-
-
-

- ${status.data.health === 'healthy' ? 'Здоров' : status.data.health === 'degraded' ? 'Деградирован' : 'Критический'} -

-

Состояние кластера

-
-
-
-
-
-

${status.data.active_nodes}/${status.data.total_nodes}

-

Активные узлы

-
-
-
-
-
-

${status.data.replication_factor}

-

Фактор репликации

-
-
-
- -
-

Узлы кластера

- - - - - - ${nodes.data.map(node => ` - - - - - - - `).join('')} - -
ID узлаАдресСтатусПоследний контакт
${escapeHtml(node.id)}${escapeHtml(node.ip)}:${node.port}${node.status}${new Date(node.last_seen * 1000).toLocaleString()}
-
- `; - } catch (error) { - contentArea.innerHTML = '
Ошибка загрузки информации о кластере
'; - } -} - -// Загрузка лога аудита -async function loadAuditLog() { - pageTitle.textContent = 'Лог аудита'; - contentArea.innerHTML = '

Загрузка лога аудита...

'; - contentArea.innerHTML = '
Функция в разработке
'; -} - -// Загрузка настроек -function loadSettings() { - pageTitle.textContent = 'Настройки'; - contentArea.innerHTML = ` -
-

Настройки интерфейса

-
- - -
- -
- `; -} - -// Обработка CRUD действий -function handleCrudAction(action) { - switch(action) { - case 'create-db': - showCreateDatabaseModal(); - break; - case 'create-collection': - showCreateCollectionModal(); - break; - case 'insert-doc': - showInsertDocumentModal(); - break; - case 'find-doc': - showFindDocumentModal(); - break; - case 'update-doc': - showUpdateDocumentModal(); - break; - case 'delete-doc': - showDeleteDocumentModal(); - break; - } -} - -// Показать модальное окно создания БД -function showCreateDatabaseModal() { - modalTitle.textContent = 'Создать базу данных'; - modalConfirm.textContent = 'Подтвердить'; - modalBody.innerHTML = ` -
- - -
- `; - - modal.classList.add('show'); - - modalConfirm.onclick = async () => { - const dbName = document.getElementById('dbName').value; - if (!dbName) { - showNotification('Введите имя базы данных', 'error'); - return; - } - - try { - const response = await fetch('/api/db/' + dbName, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({}) - }); - - if (response.ok) { - modal.classList.remove('show'); - showNotification(`База данных "${dbName}" создана`, 'success'); - loadDashboard(); - } else { - const error = await response.json(); - showNotification(error.error || 'Ошибка создания БД', 'error'); - } - } catch (error) { - showNotification('Ошибка подключения', 'error'); - } - }; -} - -// Показать модальное окно создания коллекции -function showCreateCollectionModal() { - if (!currentDatabase) { - showNotification('Сначала выберите базу данных', 'warning'); - return; - } - - modalTitle.textContent = 'Создать коллекцию'; - modalConfirm.textContent = 'Подтвердить'; - modalBody.innerHTML = ` -
- - -
-
- - -
- `; - - modal.classList.add('show'); - - modalConfirm.onclick = async () => { - const collName = document.getElementById('collName').value; - if (!collName) { - showNotification('Введите имя коллекции', 'error'); - return; - } - - try { - const response = await fetch(`/api/db/${currentDatabase}/${collName}`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({}) - }); - - if (response.ok) { - modal.classList.remove('show'); - showNotification(`Коллекция "${collName}" создана`, 'success'); - viewDatabase(currentDatabase); - } else { - const error = await response.json(); - showNotification(error.error || 'Ошибка создания коллекции', 'error'); - } - } catch (error) { - showNotification('Ошибка подключения', 'error'); - } - }; -} - -// Показать модальное окно вставки документа -function showInsertDocumentModal() { - if (!currentDatabase || !currentCollection) { - showNotification('Сначала выберите базу данных и коллекцию', 'warning'); - return; - } - - modalTitle.textContent = 'Вставить документ'; - modalConfirm.textContent = 'Подтвердить'; - modalBody.innerHTML = ` -
- - -
-
- - -
-
- - -
- `; - - modal.classList.add('show'); - - modalConfirm.onclick = async () => { - const docData = document.getElementById('docData').value; - if (!docData) { - showNotification('Введите данные документа', 'error'); - return; - } - - try { - const data = JSON.parse(docData); - const response = await fetch(`/api/webui/documents/${currentDatabase}/${currentCollection}`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data) - }); - - const result = await response.json(); - - if (result.success) { - modal.classList.remove('show'); - showNotification('Документ вставлен', 'success'); - viewCollection(currentDatabase, currentCollection); - } else { - showNotification(result.error || 'Ошибка вставки документа', 'error'); - } - } catch (error) { - if (error instanceof SyntaxError) { - showNotification('Неверный формат JSON', 'error'); - } else { - showNotification('Ошибка подключения', 'error'); - } - } - }; -} - -// Показать модальное окно поиска документа -function showFindDocumentModal() { - if (!currentDatabase || !currentCollection) { - showNotification('Сначала выберите базу данных и коллекцию', 'warning'); - return; - } - - modalTitle.textContent = 'Найти документ'; - modalConfirm.textContent = 'Найти'; - modalBody.innerHTML = ` -
- - -
-
- - -
-
- - -
- `; - - modal.classList.add('show'); - - modalConfirm.onclick = async () => { - const docId = document.getElementById('docId').value; - if (!docId) { - showNotification('Введите ID документа', 'error'); - return; - } - - try { - const response = await fetch(`/api/db/${currentDatabase}/${currentCollection}/${docId}`); - - if (response.ok) { - const data = await response.json(); - modal.classList.remove('show'); - - contentArea.innerHTML = ` -
-

Результат поиска

-
-                            ${escapeHtml(JSON.stringify(data.data, null, 2))}
-                        
- -
- `; - } else { - const error = await response.json(); - showNotification(error.error || 'Документ не найден', 'error'); - } - } catch (error) { - showNotification('Ошибка подключения', 'error'); - } - }; -} - -// Показать модальное окно обновления документа -function showUpdateDocumentModal(docId, currentFields = null) { - if (!currentDatabase || !currentCollection) { - showNotification('Сначала выберите базу данных и коллекцию', 'warning'); - return; - } - - const fieldsJson = currentFields ? JSON.stringify(currentFields, null, 2) : ''; - - modalTitle.textContent = 'Обновить документ'; - modalConfirm.textContent = 'Обновить'; - modalBody.innerHTML = ` -
- - -
-
- - -
-
- - -
-
- - -
- `; - - modal.classList.add('show'); - - modalConfirm.onclick = async () => { - const updateDocId = document.getElementById('updateDocId').value; - const updateData = document.getElementById('updateData').value; - - if (!updateDocId) { - showNotification('Введите ID документа', 'error'); - return; - } - if (!updateData) { - showNotification('Введите данные для обновления', 'error'); - return; - } - - try { - const data = JSON.parse(updateData); - const response = await fetch(`/api/webui/documents/${currentDatabase}/${currentCollection}?id=${encodeURIComponent(updateDocId)}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data) - }); - - const result = await response.json(); - - if (result.success) { - modal.classList.remove('show'); - showNotification('Документ обновлён', 'success'); - viewCollection(currentDatabase, currentCollection); - } else { - showNotification(result.error || 'Ошибка обновления документа', 'error'); - } - } catch (error) { - if (error instanceof SyntaxError) { - showNotification('Неверный формат JSON', 'error'); - } else { - showNotification('Ошибка подключения', 'error'); - } - } - }; -} - -// Показать модальное окно удаления документа -function showDeleteDocumentModal() { - if (!currentDatabase || !currentCollection) { - showNotification('Сначала выберите базу данных и коллекцию', 'warning'); - return; - } - - modalTitle.textContent = 'Удалить документ'; - modalConfirm.textContent = 'Удалить'; - modalBody.innerHTML = ` -
- - -
-
- - -
-
- - -
- `; - - modal.classList.add('show'); - - modalConfirm.onclick = async () => { - const docId = document.getElementById('deleteDocId').value; - if (!docId) { - showNotification('Введите ID документа', 'error'); - return; - } - - try { - const response = await fetch(`/api/webui/documents/${currentDatabase}/${currentCollection}?id=${encodeURIComponent(docId)}`, { - method: 'DELETE' - }); - - const result = await response.json(); - - if (result.success) { - modal.classList.remove('show'); - showNotification('Документ удалён', 'success'); - viewCollection(currentDatabase, currentCollection); - } else { - showNotification(result.error || 'Ошибка удаления документа', 'error'); - } - } catch (error) { - showNotification('Ошибка подключения', 'error'); - } - }; -} - -// Удаление коллекции -window.deleteCollection = async function(dbName, collName) { - if (confirm(`Вы уверены, что хотите удалить коллекцию "${collName}"? Это действие необратимо.`)) { - try { - const response = await fetch(`/api/db/${dbName}/${collName}`, { - method: 'DELETE' - }); - - if (response.ok) { - showNotification(`Коллекция "${collName}" удалена`, 'success'); - viewDatabase(dbName); - } else { - const error = await response.json(); - showNotification(error.error || 'Ошибка удаления коллекции', 'error'); - } - } catch (error) { - showNotification('Ошибка подключения', 'error'); - } - } -}; - -// Удаление документа -window.deleteDocument = async function(dbName, collName, docId) { - if (confirm(`Вы уверены, что хотите удалить документ "${docId}"?`)) { - try { - const response = await fetch(`/api/webui/documents/${dbName}/${collName}?id=${encodeURIComponent(docId)}`, { - method: 'DELETE' - }); - - const result = await response.json(); - - if (result.success) { - showNotification('Документ удалён', 'success'); - viewCollection(dbName, collName); - } else { - showNotification(result.error || 'Ошибка удаления документа', 'error'); - } - } catch (error) { - showNotification('Ошибка подключения', 'error'); - } - } -}; - -// Сохранение настроек -function saveSettings() { - const theme = document.getElementById('themeSelect')?.value; - if (theme) { - localStorage.setItem('theme', theme); - showNotification('Настройки сохранены', 'success'); - } -} - -// Утилиты -function escapeHtml(str) { - if (!str) return ''; - return String(str) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - -function showNotification(message, type = 'info') { - const container = document.getElementById('notificationContainer'); - const notification = document.createElement('div'); - notification.className = `notification ${type}`; - - let icon = ''; - switch(type) { - case 'success': icon = ''; break; - case 'error': icon = ''; break; - case 'warning': icon = ''; break; - default: icon = ''; - } - - notification.innerHTML = `${icon}${escapeHtml(message)}`; - container.appendChild(notification); - - setTimeout(() => { - notification.style.animation = 'slideOutRight 0.3s ease'; - setTimeout(() => notification.remove(), 300); - }, 3000); -} - -function setActiveNav(activeLink) { - document.querySelectorAll('.nav-link').forEach(link => { - link.classList.remove('active'); - }); - activeLink.classList.add('active'); -}