// LOGPile Frontend Application
document.addEventListener('DOMContentLoaded', () => {
initUpload();
initTabs();
initFilters();
});
// Upload handling
function initUpload() {
const dropZone = document.getElementById('drop-zone');
const fileInput = document.getElementById('file-input');
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
uploadFile(files[0]);
}
});
fileInput.addEventListener('change', () => {
if (fileInput.files.length > 0) {
uploadFile(fileInput.files[0]);
}
});
}
async function uploadFile(file) {
const status = document.getElementById('upload-status');
status.textContent = 'Загрузка и анализ...';
status.className = '';
const formData = new FormData();
formData.append('archive', file);
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
if (response.ok) {
status.innerHTML = `${escapeHtml(result.vendor)}
` +
`${result.stats.sensors} сенсоров, ${result.stats.fru} компонентов, ${result.stats.events} событий`;
status.className = 'success';
loadData(result.vendor);
} else {
status.textContent = result.error || 'Ошибка загрузки';
status.className = 'error';
}
} catch (err) {
status.textContent = 'Ошибка соединения';
status.className = 'error';
}
}
// Tab navigation
function initTabs() {
const tabs = document.querySelectorAll('.tab');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
tab.classList.add('active');
document.getElementById(tab.dataset.tab).classList.add('active');
});
});
}
// Filters
function initFilters() {
document.getElementById('sensor-filter').addEventListener('change', (e) => {
filterSensors(e.target.value);
});
document.getElementById('severity-filter').addEventListener('change', (e) => {
filterEvents(e.target.value);
});
document.getElementById('serial-filter').addEventListener('change', (e) => {
filterSerials(e.target.value);
});
}
let allSensors = [];
let allEvents = [];
let allSerials = [];
let currentVendor = '';
// Load data from API
async function loadData(vendor) {
currentVendor = vendor || '';
document.getElementById('upload-section').classList.add('hidden');
document.getElementById('data-section').classList.remove('hidden');
document.getElementById('clear-btn').classList.remove('hidden');
// Update vendor badge if exists
const vendorBadge = document.getElementById('vendor-badge');
if (vendorBadge && currentVendor) {
vendorBadge.textContent = currentVendor;
vendorBadge.classList.remove('hidden');
}
await Promise.all([
loadConfig(),
loadFirmware(),
loadSensors(),
loadSerials(),
loadEvents()
]);
}
async function loadConfig() {
try {
const response = await fetch('/api/config');
const config = await response.json();
renderConfig(config);
} catch (err) {
console.error('Failed to load config:', err);
}
}
function renderConfig(data) {
const container = document.getElementById('config-content');
if (!data || Object.keys(data).length === 0) {
container.innerHTML = '
Нет данных о конфигурации
'; return; } const config = data.hardware || data; const spec = data.specification; let html = ''; // Configuration sub-tabs html += `Нет данных о спецификации
'; } html += '| Socket | Модель | Ядра | Потоки | Частота | Max Turbo | TDP | L3 Cache | PPIN |
|---|---|---|---|---|---|---|---|---|
| CPU${cpu.socket} | ${escapeHtml(cpu.model)} | ${cpu.cores} | ${cpu.threads} | ${cpu.frequency_mhz} MHz | ${cpu.max_frequency_mhz} MHz | ${cpu.tdp_w}W | ${Math.round(cpu.l3_cache_kb/1024)} MB | ${escapeHtml(cpu.ppin || '-')} |
Нет данных о процессорах
'; } html += '| Location | Наличие | Размер | Тип | Max частота | Текущая частота | Производитель | Артикул | Статус |
|---|---|---|---|---|---|---|---|---|
| ${escapeHtml(mem.location || mem.slot)} | ${present} | ${sizeGB} GB | ${escapeHtml(mem.type || '-')} | ${mem.max_speed_mhz || '-'} MHz | ${mem.current_speed_mhz || mem.speed_mhz || '-'} MHz | ${escapeHtml(mem.manufacturer || '-')} | ${escapeHtml(mem.part_number || '-')} |
${escapeHtml(mem.status || 'OK')} |
Нет данных о памяти
'; } html += '| Слот | Производитель | Модель | Мощность | Вход | Выход | Напряжение | Температура | Статус |
|---|---|---|---|---|---|---|---|---|
| ${escapeHtml(psu.slot)} | ${escapeHtml(psu.vendor || '-')} | ${escapeHtml(psu.model || '-')} | ${psu.wattage_w || '-'}W | ${psu.input_power_w || '-'}W | ${psu.output_power_w || '-'}W | ${psu.input_voltage ? psu.input_voltage.toFixed(0) : '-'}V | ${psu.temperature_c || '-'}°C | ${escapeHtml(psu.status || '-')} |
Нет данных о блоках питания
'; } html += '| Слот | Тип | Интерфейс | Модель | Производитель | Размер | Серийный номер |
|---|---|---|---|---|---|---|
| ${escapeHtml(s.slot || '-')} | ${escapeHtml(s.type || '-')} | ${escapeHtml(s.interface || '-')} | ${escapeHtml(s.model || '-')} | ${escapeHtml(s.manufacturer || '-')} | ${s.size_gb} GB | ${escapeHtml(s.serial_number || '-')} |
Нет данных о накопителях
'; } html += '| Слот | Модель | Производитель | BDF | PCIe | Серийный номер |
|---|---|---|---|---|---|
| ${escapeHtml(gpu.slot || '-')} | ${escapeHtml(gpu.model || '-')} | ${escapeHtml(gpu.manufacturer || '-')} | ${escapeHtml(gpu.bdf || '-')} |
x${gpu.link_width || '-'} ${escapeHtml(gpu.link_speed || '-')} | ${escapeHtml(gpu.serial_number || '-')} |
Нет GPU
'; } html += '| Слот | Модель | Производитель | Порты | Тип | MAC адреса | Статус |
|---|---|---|---|---|---|---|
| ${escapeHtml(nic.location || nic.slot || '-')} | ${escapeHtml(nic.model || '-')} | ${escapeHtml(nic.vendor || '-')} | ${nic.port_count || '-'} | ${escapeHtml(nic.port_type || '-')} | ${escapeHtml(macs)} |
${escapeHtml(nic.status || '-')} |
Нет данных о сетевых адаптерах
'; } html += '| Слот | BDF | Тип | Производитель | Vendor:Device ID | PCIe Link |
|---|---|---|---|---|---|
| ${escapeHtml(p.slot || '-')} | ${escapeHtml(p.bdf || '-')} |
${escapeHtml(p.device_class || '-')} | ${escapeHtml(p.manufacturer || '-')} | ${p.vendor_id ? p.vendor_id.toString(16) : '-'}:${p.device_id ? p.device_id.toString(16) : '-'} |
x${p.link_width || '-'} ${escapeHtml(p.link_speed || '-')} |
Нет данных о PCIe устройствах
'; } html += '${escapeHtml(fw.version)}Нет данных о сенсорах
'; return; } // Group by type const byType = {}; sensors.forEach(s => { if (!byType[s.type]) byType[s.type] = []; byType[s.type].push(s); }); const typeNames = { temperature: 'Температура', voltage: 'Напряжение', power: 'Мощность', fan_speed: 'Вентиляторы', fan_status: 'Статус вентиляторов', psu_status: 'Статус БП', cpu_status: 'Статус CPU', storage_status: 'Статус накопителей', other: 'Прочее' }; let html = ''; for (const [type, items] of Object.entries(byType)) { html += `${escapeHtml(item.serial_number)}