docs: add comprehensive JSDoc comments to all JavaScript functions
This commit is contained in:
parent
a49714ab14
commit
f5926b80ad
@ -1,4 +1,13 @@
|
|||||||
// Theme toggle functionality
|
/**
|
||||||
|
* LogBoard+ - Скрипт страницы ошибок
|
||||||
|
* Автор: Сергей Антропов
|
||||||
|
* Сайт: https://devops.org.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Переключает тему между светлой и темной
|
||||||
|
* Обновляет атрибут data-theme и сохраняет выбор в localStorage
|
||||||
|
*/
|
||||||
function toggleTheme() {
|
function toggleTheme() {
|
||||||
const html = document.documentElement;
|
const html = document.documentElement;
|
||||||
const currentTheme = html.getAttribute('data-theme');
|
const currentTheme = html.getAttribute('data-theme');
|
||||||
@ -14,7 +23,10 @@ function toggleTheme() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize theme on page load
|
/**
|
||||||
|
* Инициализация темы при загрузке страницы
|
||||||
|
* Загружает сохраненную тему из localStorage
|
||||||
|
*/
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const savedTheme = localStorage.getItem('lb_theme') || 'dark';
|
const savedTheme = localStorage.getItem('lb_theme') || 'dark';
|
||||||
document.documentElement.setAttribute('data-theme', savedTheme);
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
||||||
@ -26,7 +38,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Keyboard shortcut for theme toggle (Ctrl+T)
|
/**
|
||||||
|
* Горячая клавиша для переключения темы (Ctrl+T)
|
||||||
|
*/
|
||||||
document.addEventListener('keydown', function(e) {
|
document.addEventListener('keydown', function(e) {
|
||||||
if (e.ctrlKey && e.key === 't') {
|
if (e.ctrlKey && e.key === 't') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -1,62 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* LogBoard+ - Веб-панель для просмотра логов микросервисов
|
||||||
|
* Автор: Сергей Антропов
|
||||||
|
* Сайт: https://devops.org.ru
|
||||||
|
* Версия: 2.0
|
||||||
|
*/
|
||||||
|
|
||||||
console.log('LogBoard+ script loaded - VERSION 2');
|
console.log('LogBoard+ script loaded - VERSION 2');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Глобальное состояние приложения
|
||||||
|
* Содержит все данные о контейнерах, настройках и режимах отображения
|
||||||
|
*/
|
||||||
const state = {
|
const state = {
|
||||||
services: [],
|
services: [], // Список всех доступных сервисов
|
||||||
current: null,
|
current: null, // Текущий выбранный контейнер для single view
|
||||||
open: {}, // id -> {ws, logEl, wrapEl, counters, pausedBuffer:[], serviceName}
|
open: {}, // Открытые WebSocket соединения: id -> {ws, logEl, wrapEl, counters, pausedBuffer:[], serviceName}
|
||||||
layout: 'tabs', // 'tabs' | 'grid2' | 'grid3' | 'grid4'
|
layout: 'tabs', // Режим отображения: 'tabs' | 'grid2' | 'grid3' | 'grid4'
|
||||||
filter: null,
|
filter: null, // Текущий фильтр для логов
|
||||||
levels: {debug:true, info:true, warn:true, err:true, other:true},
|
levels: {debug:true, info:true, warn:true, err:true, other:true}, // Уровни логирования для отображения
|
||||||
selectedContainers: [], // Массив ID выбранных контейнеров для мультипросмотра
|
selectedContainers: [], // Массив ID выбранных контейнеров для мультипросмотра
|
||||||
multiViewMode: false, // Режим мультипросмотра
|
multiViewMode: false, // Режим мультипросмотра (true = multi-view, false = single-view)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ссылки на DOM элементы интерфейса
|
||||||
|
* Содержит все элементы управления и отображения
|
||||||
|
*/
|
||||||
const els = {
|
const els = {
|
||||||
// Legacy elements
|
// Legacy elements (старые элементы для обратной совместимости)
|
||||||
tabs: document.getElementById('tabs'),
|
tabs: document.getElementById('tabs'), // Контейнер с вкладками
|
||||||
grid: document.getElementById('grid'),
|
grid: document.getElementById('grid'), // Контейнер с сеткой
|
||||||
tail: document.getElementById('tail'),
|
tail: document.getElementById('tail'), // Поле ввода количества строк логов
|
||||||
autoscroll: document.getElementById('autoscroll'),
|
autoscroll: document.getElementById('autoscroll'), // Чекбокс автопрокрутки
|
||||||
wrapToggle: document.getElementById('wrap'),
|
wrapToggle: document.getElementById('wrap'), // Переключатель переноса строк
|
||||||
|
|
||||||
filter: document.getElementById('filter'),
|
filter: document.getElementById('filter'), // Поле фильтра логов
|
||||||
wsstate: document.getElementById('wsstate'),
|
wsstate: document.getElementById('wsstate'), // Индикатор состояния WebSocket
|
||||||
ajaxUpdateBtn: document.getElementById('ajaxUpdateBtn'),
|
ajaxUpdateBtn: document.getElementById('ajaxUpdateBtn'), // Кнопка AJAX обновления
|
||||||
projectBadge: document.getElementById('projectBadge'),
|
projectBadge: document.getElementById('projectBadge'), // Бейдж текущего проекта
|
||||||
|
|
||||||
clearBtn: document.getElementById('clear'),
|
clearBtn: document.getElementById('clear'), // Кнопка очистки логов
|
||||||
refreshBtn: document.getElementById('refresh'),
|
refreshBtn: document.getElementById('refresh'), // Кнопка обновления
|
||||||
snapshotBtn: document.getElementById('snapshot'),
|
snapshotBtn: document.getElementById('snapshot'), // Кнопка создания снимка
|
||||||
lvlDebug: document.getElementById('lvlDebug'),
|
lvlDebug: document.getElementById('lvlDebug'), // Кнопка уровня DEBUG
|
||||||
lvlInfo: document.getElementById('lvlInfo'),
|
lvlInfo: document.getElementById('lvlInfo'), // Кнопка уровня INFO
|
||||||
lvlWarn: document.getElementById('lvlWarn'),
|
lvlWarn: document.getElementById('lvlWarn'), // Кнопка уровня WARN
|
||||||
lvlErr: document.getElementById('lvlErr'),
|
lvlErr: document.getElementById('lvlErr'), // Кнопка уровня ERROR
|
||||||
lvlOther: document.getElementById('lvlOther'),
|
lvlOther: document.getElementById('lvlOther'), // Кнопка уровня OTHER
|
||||||
layoutBadge: document.getElementById('layoutBadge') || { textContent: '' },
|
layoutBadge: document.getElementById('layoutBadge') || { textContent: '' }, // Бейдж режима отображения
|
||||||
aggregate: document.getElementById('aggregate') || { checked: false },
|
aggregate: document.getElementById('aggregate') || { checked: false }, // Чекбокс агрегации
|
||||||
themeSwitch: document.getElementById('themeSwitch'),
|
themeSwitch: document.getElementById('themeSwitch'), // Переключатель темы
|
||||||
copyFab: document.getElementById('copyFab'),
|
copyFab: document.getElementById('copyFab'), // Кнопка копирования
|
||||||
groupBtn: document.getElementById('groupBtn') || { onclick: null },
|
groupBtn: document.getElementById('groupBtn') || { onclick: null }, // Кнопка группировки
|
||||||
|
|
||||||
// New modern elements
|
// New modern elements (новые элементы современного интерфейса)
|
||||||
containerList: document.getElementById('containerList'),
|
containerList: document.getElementById('containerList'), // Список контейнеров
|
||||||
logContent: document.getElementById('logContent'),
|
logContent: document.getElementById('logContent'), // Основной контент логов
|
||||||
mobileToggle: document.getElementById('mobileToggle'),
|
mobileToggle: document.getElementById('mobileToggle'), // Переключатель мобильного режима
|
||||||
optionsBtn: document.getElementById('optionsBtn'),
|
optionsBtn: document.getElementById('optionsBtn'), // Кнопка настроек
|
||||||
helpBtn: document.getElementById('helpBtn'),
|
helpBtn: document.getElementById('helpBtn'), // Кнопка помощи
|
||||||
logoutBtn: document.getElementById('logoutBtn'),
|
logoutBtn: document.getElementById('logoutBtn'), // Кнопка выхода
|
||||||
sidebar: document.getElementById('sidebar'),
|
sidebar: document.getElementById('sidebar'), // Боковая панель
|
||||||
sidebarToggle: document.getElementById('sidebarToggle'),
|
sidebarToggle: document.getElementById('sidebarToggle'), // Переключатель боковой панели
|
||||||
header: document.getElementById('header'),
|
header: document.getElementById('header'), // Заголовок
|
||||||
hotkeysModal: document.getElementById('hotkeysModal'),
|
hotkeysModal: document.getElementById('hotkeysModal'), // Модальное окно горячих клавиш
|
||||||
hotkeysModalClose: document.getElementById('hotkeysModalClose'),
|
hotkeysModalClose: document.getElementById('hotkeysModalClose'), // Кнопка закрытия модального окна
|
||||||
multiViewPanel: document.getElementById('multiViewPanel'),
|
multiViewPanel: document.getElementById('multiViewPanel'), // Панель мультипросмотра
|
||||||
multiViewPanelTitle: document.getElementById('multiViewPanelTitle'),
|
multiViewPanelTitle: document.getElementById('multiViewPanelTitle'), // Заголовок мультипросмотра
|
||||||
singleViewPanel: document.getElementById('singleViewPanel'),
|
singleViewPanel: document.getElementById('singleViewPanel'), // Панель одиночного просмотра
|
||||||
singleViewTitle: document.getElementById('singleViewTitle'),
|
singleViewTitle: document.getElementById('singleViewTitle'), // Заголовок одиночного просмотра
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----- Theme toggle -----
|
/**
|
||||||
|
* Инициализация переключателя темы
|
||||||
|
* Загружает сохраненную тему из localStorage и настраивает переключатель
|
||||||
|
*/
|
||||||
(function initTheme(){
|
(function initTheme(){
|
||||||
const saved = localStorage.lb_theme || 'dark';
|
const saved = localStorage.lb_theme || 'dark';
|
||||||
document.documentElement.setAttribute('data-theme', saved);
|
document.documentElement.setAttribute('data-theme', saved);
|
||||||
@ -68,6 +86,10 @@ const els = {
|
|||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Устанавливает состояние WebSocket соединения в интерфейсе
|
||||||
|
* @param {string} s - Состояние: 'on', 'off', 'err', 'available'
|
||||||
|
*/
|
||||||
function setWsState(s){
|
function setWsState(s){
|
||||||
console.log('setWsState: Устанавливаем состояние', s);
|
console.log('setWsState: Устанавливаем состояние', s);
|
||||||
console.log('setWsState: Текущие соединения:', Object.keys(state.open));
|
console.log('setWsState: Текущие соединения:', Object.keys(state.open));
|
||||||
@ -89,7 +111,10 @@ function setWsState(s){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для определения общего состояния WebSocket соединений
|
/**
|
||||||
|
* Определяет общее состояние WebSocket соединений
|
||||||
|
* Проверяет все открытые соединения и устанавливает соответствующее состояние
|
||||||
|
*/
|
||||||
function determineWsState() {
|
function determineWsState() {
|
||||||
const openConnections = Object.keys(state.open);
|
const openConnections = Object.keys(state.open);
|
||||||
|
|
||||||
@ -238,6 +263,10 @@ function stopWebSocketStatusCheck() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Устанавливает визуальное состояние кнопки AJAX обновления
|
||||||
|
* @param {boolean} enabled - Включено ли AJAX обновление
|
||||||
|
*/
|
||||||
function setAjaxUpdateState(enabled) {
|
function setAjaxUpdateState(enabled) {
|
||||||
console.log('setAjaxUpdateState: enabled =', enabled, 'els.ajaxUpdateBtn =', !!els.ajaxUpdateBtn);
|
console.log('setAjaxUpdateState: enabled =', enabled, 'els.ajaxUpdateBtn =', !!els.ajaxUpdateBtn);
|
||||||
|
|
||||||
@ -260,7 +289,10 @@ function setAjaxUpdateState(enabled) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для обновления всех логов при изменении фильтров
|
/**
|
||||||
|
* Обновляет отображение всех логов при изменении фильтров
|
||||||
|
* Перерисовывает логи с учетом текущих настроек фильтрации и уровней
|
||||||
|
*/
|
||||||
function refreshAllLogs() {
|
function refreshAllLogs() {
|
||||||
// Обновляем обычный просмотр
|
// Обновляем обычный просмотр
|
||||||
Object.keys(state.open).forEach(id => {
|
Object.keys(state.open).forEach(id => {
|
||||||
@ -339,8 +371,19 @@ function refreshAllLogs() {
|
|||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Экранирует HTML символы для безопасного отображения
|
||||||
|
* @param {string} s - Строка для экранирования
|
||||||
|
* @returns {string} Экранированная строка
|
||||||
|
*/
|
||||||
function escapeHtml(s){ return s.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); }
|
function escapeHtml(s){ return s.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Классифицирует строку лога по уровню логирования
|
||||||
|
* Определяет уровень на основе ключевых слов и паттернов в строке
|
||||||
|
* @param {string} line - Строка лога для классификации
|
||||||
|
* @returns {string} Класс уровня: 'dbg', 'err', 'warn', 'ok', 'other'
|
||||||
|
*/
|
||||||
function classify(line){
|
function classify(line){
|
||||||
const l = line.toLowerCase();
|
const l = line.toLowerCase();
|
||||||
|
|
||||||
@ -374,6 +417,11 @@ function classify(line){
|
|||||||
|
|
||||||
return 'other';
|
return 'other';
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Проверяет, разрешен ли отображение лога данного уровня
|
||||||
|
* @param {string} cls - Класс уровня лога ('dbg', 'err', 'warn', 'ok', 'other')
|
||||||
|
* @returns {boolean} Разрешен ли отображение
|
||||||
|
*/
|
||||||
function allowedByLevel(cls){
|
function allowedByLevel(cls){
|
||||||
if (cls==='dbg') return state.levels.debug;
|
if (cls==='dbg') return state.levels.debug;
|
||||||
if (cls==='err') return state.levels.err;
|
if (cls==='err') return state.levels.err;
|
||||||
@ -383,7 +431,13 @@ function allowedByLevel(cls){
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для проверки уровня логирования для конкретного контейнера
|
/**
|
||||||
|
* Проверяет, разрешен ли отображение лога данного уровня для конкретного контейнера
|
||||||
|
* Используется в режиме мультипросмотра для индивидуальных настроек контейнеров
|
||||||
|
* @param {string} cls - Класс уровня лога ('dbg', 'err', 'warn', 'ok', 'other')
|
||||||
|
* @param {string} containerId - ID контейнера
|
||||||
|
* @returns {boolean} Разрешен ли отображение
|
||||||
|
*/
|
||||||
function allowedByContainerLevel(cls, containerId) {
|
function allowedByContainerLevel(cls, containerId) {
|
||||||
// Если настройки контейнера не инициализированы, инициализируем их
|
// Если настройки контейнера не инициализированы, инициализируем их
|
||||||
if (!state.containerLevels) {
|
if (!state.containerLevels) {
|
||||||
@ -408,7 +462,11 @@ function allowedByContainerLevel(cls, containerId) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для обновления видимости логов в Single View
|
/**
|
||||||
|
* Обновляет видимость логов в Single View режиме
|
||||||
|
* Перерисовывает логи с учетом текущих фильтров и настроек уровней
|
||||||
|
* @param {HTMLElement} logElement - Элемент для обновления
|
||||||
|
*/
|
||||||
function updateLogVisibility(logElement) {
|
function updateLogVisibility(logElement) {
|
||||||
if (!logElement || !state.current) return;
|
if (!logElement || !state.current) return;
|
||||||
|
|
||||||
@ -616,6 +674,12 @@ function initializeLevelButtons() {
|
|||||||
// Применяем настройки wrap text
|
// Применяем настройки wrap text
|
||||||
applyWrapSettings();
|
applyWrapSettings();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Применяет фильтр к строке лога
|
||||||
|
* Проверяет, соответствует ли строка текущему фильтру (безопасный regex поиск)
|
||||||
|
* @param {string} line - Строка лога для проверки
|
||||||
|
* @returns {boolean} Проходит ли строка фильтр
|
||||||
|
*/
|
||||||
function applyFilter(line){
|
function applyFilter(line){
|
||||||
if(!state.filter) return true;
|
if(!state.filter) return true;
|
||||||
try{
|
try{
|
||||||
@ -628,14 +692,30 @@ function applyFilter(line){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANSI → HTML (SGR: 0/1/3/4, 30-37)
|
/**
|
||||||
|
* Константы и настройки для работы с ANSI цветами
|
||||||
|
* SGR (Select Graphic Rendition): 0/1/3/4, 30-37
|
||||||
|
*/
|
||||||
|
|
||||||
// ----- Instance color & filters -----
|
/**
|
||||||
const inst = { colors: {}, filters: {}, palette: [
|
* Настройки экземпляров контейнеров
|
||||||
|
* Содержит цвета, фильтры и палитру для визуального различия контейнеров
|
||||||
|
*/
|
||||||
|
const inst = {
|
||||||
|
colors: {}, // Кэш цветов для контейнеров
|
||||||
|
filters: {}, // Фильтры для экземпляров
|
||||||
|
palette: [ // Палитра цветов для контейнеров
|
||||||
'#7aa2f7','#9ece6a','#e0af68','#f7768e','#bb9af7','#7dcfff','#c0caf5','#f6bd60',
|
'#7aa2f7','#9ece6a','#e0af68','#f7768e','#bb9af7','#7dcfff','#c0caf5','#f6bd60',
|
||||||
'#84cc16','#06b6d4','#fb923c','#ef4444','#22c55e','#a855f7'
|
'#84cc16','#06b6d4','#fb923c','#ef4444','#22c55e','#a855f7'
|
||||||
]};
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Генерирует уникальный цвет для контейнера на основе его ID
|
||||||
|
* Использует хеш-функцию для детерминированного выбора цвета из палитры
|
||||||
|
* @param {string} id8 - Первые 8 символов ID контейнера
|
||||||
|
* @returns {string} HEX цвет для контейнера
|
||||||
|
*/
|
||||||
function idColor(id8){
|
function idColor(id8){
|
||||||
if (inst.colors[id8]) return inst.colors[id8];
|
if (inst.colors[id8]) return inst.colors[id8];
|
||||||
// simple hash to pick from palette
|
// simple hash to pick from palette
|
||||||
@ -679,6 +759,12 @@ function parsePrefixAndStrip(line){
|
|||||||
return {id8: m[1], rest: m[2]};
|
return {id8: m[1], rest: m[2]};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конвертирует ANSI escape-последовательности в HTML
|
||||||
|
* Поддерживает цвета (30-37), жирный (1), курсив (3), подчеркивание (4)
|
||||||
|
* @param {string} text - Текст с ANSI кодами
|
||||||
|
* @returns {string} HTML с CSS классами для стилизации
|
||||||
|
*/
|
||||||
function ansiToHtml(text){
|
function ansiToHtml(text){
|
||||||
const ESC = '\u001b[';
|
const ESC = '\u001b[';
|
||||||
const parts = text.split(ESC);
|
const parts = text.split(ESC);
|
||||||
@ -1614,6 +1700,11 @@ async function updateMultiViewMode() {
|
|||||||
updateLogLevelsVisibility();
|
updateLogLevelsVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Настраивает интерфейс для режима мультипросмотра (multi-view)
|
||||||
|
* Создает сетку панелей для одновременного просмотра нескольких контейнеров
|
||||||
|
* Открывает WebSocket соединения для всех выбранных контейнеров
|
||||||
|
*/
|
||||||
async function setupMultiView() {
|
async function setupMultiView() {
|
||||||
console.log('setupMultiView called');
|
console.log('setupMultiView called');
|
||||||
|
|
||||||
@ -1749,6 +1840,12 @@ async function setupMultiView() {
|
|||||||
updateLogLevelsVisibility();
|
updateLogLevelsVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создает панель для мультипросмотра контейнера
|
||||||
|
* Генерирует HTML структуру с заголовком, кнопками уровней и областью логов
|
||||||
|
* @param {Object} service - Объект сервиса/контейнера
|
||||||
|
* @returns {HTMLElement} Созданная панель мультипросмотра
|
||||||
|
*/
|
||||||
function createMultiViewPanel(service) {
|
function createMultiViewPanel(service) {
|
||||||
console.log(`Creating multi-view panel for service: ${service.name} (${service.id})`);
|
console.log(`Creating multi-view panel for service: ${service.name} (${service.id})`);
|
||||||
const panel = document.createElement('div');
|
const panel = document.createElement('div');
|
||||||
@ -1833,6 +1930,11 @@ function createMultiViewPanel(service) {
|
|||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Открывает WebSocket соединение для контейнера в режиме мультипросмотра
|
||||||
|
* Настраивает обработчики сообщений и управляет отображением логов
|
||||||
|
* @param {Object} service - Объект сервиса/контейнера
|
||||||
|
*/
|
||||||
function openMultiViewWs(service) {
|
function openMultiViewWs(service) {
|
||||||
const containerId = service.id;
|
const containerId = service.id;
|
||||||
console.log(`openMultiViewWs: Starting WebSocket setup for ${service.name} (${containerId})`);
|
console.log(`openMultiViewWs: Starting WebSocket setup for ${service.name} (${containerId})`);
|
||||||
@ -2099,6 +2201,10 @@ function wsUrl(containerId, service, project){
|
|||||||
return `${proto}://${location.host}/api/websocket/logs/${encodeURIComponent(containerId)}?tail=${tail}&token=${token}${sp}${pj}`;
|
return `${proto}://${location.host}/api/websocket/logs/${encodeURIComponent(containerId)}?tail=${tail}&token=${token}${sp}${pj}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Закрывает WebSocket соединение для контейнера
|
||||||
|
* @param {string} id - ID контейнера
|
||||||
|
*/
|
||||||
function closeWs(id){
|
function closeWs(id){
|
||||||
const o = state.open[id];
|
const o = state.open[id];
|
||||||
if (!o) return;
|
if (!o) return;
|
||||||
@ -2107,6 +2213,11 @@ function closeWs(id){
|
|||||||
delete state.open[id];
|
delete state.open[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создает и скачивает снимок логов контейнера
|
||||||
|
* В режиме мультипросмотра создает отдельные файлы для каждого контейнера
|
||||||
|
* @param {string} id - ID контейнера
|
||||||
|
*/
|
||||||
async function sendSnapshot(id){
|
async function sendSnapshot(id){
|
||||||
const o = state.open[id];
|
const o = state.open[id];
|
||||||
if (!o){ alert('not open'); return; }
|
if (!o){ alert('not open'); return; }
|
||||||
@ -2222,6 +2333,12 @@ async function sendSnapshot(id){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Открывает WebSocket соединение для контейнера
|
||||||
|
* Настраивает обработчики событий и управляет отображением логов
|
||||||
|
* @param {Object} svc - Объект сервиса/контейнера
|
||||||
|
* @param {HTMLElement} panel - Панель для отображения логов
|
||||||
|
*/
|
||||||
function openWs(svc, panel){
|
function openWs(svc, panel){
|
||||||
const id = svc.id;
|
const id = svc.id;
|
||||||
console.log(`openWs: Called for ${svc.name} (${id}) in multiViewMode: ${state.multiViewMode}`);
|
console.log(`openWs: Called for ${svc.name} (${id}) in multiViewMode: ${state.multiViewMode}`);
|
||||||
@ -2979,7 +3096,12 @@ function checkMultiViewHTML() {
|
|||||||
console.log('=== Конец проверки HTML ===');
|
console.log('=== Конец проверки HTML ===');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Глобальная функция для обработки логов
|
/**
|
||||||
|
* Основная функция обработки строк логов
|
||||||
|
* Классифицирует, фильтрует и отображает строки логов в зависимости от режима
|
||||||
|
* @param {string} id - ID контейнера
|
||||||
|
* @param {string} line - Строка лога для обработки
|
||||||
|
*/
|
||||||
function handleLine(id, line){
|
function handleLine(id, line){
|
||||||
|
|
||||||
const obj = state.open[id];
|
const obj = state.open[id];
|
||||||
@ -3191,6 +3313,11 @@ function ensurePanel(svc){
|
|||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Переключает интерфейс в режим одиночного просмотра (single view)
|
||||||
|
* Закрывает мультипросмотр, открывает WebSocket для выбранного контейнера
|
||||||
|
* @param {Object} svc - Объект сервиса/контейнера для просмотра
|
||||||
|
*/
|
||||||
async function switchToSingle(svc){
|
async function switchToSingle(svc){
|
||||||
console.log('switchToSingle: ENTRY POINT - function called - VERSION 2');
|
console.log('switchToSingle: ENTRY POINT - function called - VERSION 2');
|
||||||
console.log('switchToSingle: svc parameter:', svc);
|
console.log('switchToSingle: svc parameter:', svc);
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
// Theme toggle
|
/**
|
||||||
|
* LogBoard+ - Скрипт страницы входа
|
||||||
|
* Автор: Сергей Антропов
|
||||||
|
* Сайт: https://devops.org.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация переключателя темы
|
||||||
|
* Загружает сохраненную тему из localStorage и настраивает переключатель
|
||||||
|
*/
|
||||||
(function initTheme(){
|
(function initTheme(){
|
||||||
const saved = localStorage.lb_theme || 'dark';
|
const saved = localStorage.lb_theme || 'dark';
|
||||||
document.documentElement.setAttribute('data-theme', saved);
|
document.documentElement.setAttribute('data-theme', saved);
|
||||||
@ -10,7 +19,10 @@
|
|||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Password toggle
|
/**
|
||||||
|
* Обработчик переключения видимости пароля
|
||||||
|
* Показывает/скрывает пароль и меняет иконку
|
||||||
|
*/
|
||||||
document.getElementById('passwordToggle').addEventListener('click', function() {
|
document.getElementById('passwordToggle').addEventListener('click', function() {
|
||||||
const passwordInput = document.getElementById('password');
|
const passwordInput = document.getElementById('password');
|
||||||
const icon = this.querySelector('i');
|
const icon = this.querySelector('i');
|
||||||
@ -24,7 +36,10 @@ document.getElementById('passwordToggle').addEventListener('click', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Login form
|
/**
|
||||||
|
* Обработчик отправки формы входа
|
||||||
|
* Выполняет аутентификацию пользователя через API
|
||||||
|
*/
|
||||||
document.getElementById('loginForm').addEventListener('submit', async function(e) {
|
document.getElementById('loginForm').addEventListener('submit', async function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@ -82,18 +97,27 @@ document.getElementById('loginForm').addEventListener('submit', async function(e
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Показывает сообщение об ошибке
|
||||||
|
* @param {string} message - Текст ошибки
|
||||||
|
*/
|
||||||
function showError(message) {
|
function showError(message) {
|
||||||
const errorMessage = document.getElementById('errorMessage');
|
const errorMessage = document.getElementById('errorMessage');
|
||||||
errorMessage.textContent = message;
|
errorMessage.textContent = message;
|
||||||
errorMessage.classList.add('show');
|
errorMessage.classList.add('show');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Скрывает сообщение об ошибке
|
||||||
|
*/
|
||||||
function hideError() {
|
function hideError() {
|
||||||
const errorMessage = document.getElementById('errorMessage');
|
const errorMessage = document.getElementById('errorMessage');
|
||||||
errorMessage.classList.remove('show');
|
errorMessage.classList.remove('show');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-focus on username field
|
/**
|
||||||
|
* Автофокус на поле имени пользователя при загрузке страницы
|
||||||
|
*/
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.getElementById('username').focus();
|
document.getElementById('username').focus();
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user