Add branding, license, and firmware table improvements

- Add mchus.pro branding to header and footer
- Add author info and git repository link
- Add proprietary license (Mike Chus)
- Firmware table: replace count column with separate component/model columns
- Add branding to TXT export reports

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-25 09:39:52 +03:00
parent 83378fa761
commit e52eb909f7
7 changed files with 208 additions and 15 deletions

3
.gitignore vendored
View File

@@ -59,3 +59,6 @@ go.work.sum
# env file
.env
# Distribution binaries
dist/

44
LICENSE Normal file
View File

@@ -0,0 +1,44 @@
Proprietary Software License
Copyright (c) 2025 Mike Chus (mchus.pro)
All Rights Reserved.
This software and associated documentation files (the "Software") are the
exclusive property of Mike Chus (mchus.pro).
TERMS AND CONDITIONS:
1. GRANT OF LICENSE
No license is granted to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software without explicit written
permission from the copyright holder.
2. RESTRICTIONS
You may not:
- Copy, modify, or distribute the Software
- Reverse engineer, decompile, or disassemble the Software
- Remove or alter any proprietary notices or labels on the Software
- Use the Software for commercial purposes without authorization
- Sublicense, lease, or lend the Software to third parties
3. OWNERSHIP
The Software is protected by copyright laws and international treaty
provisions. The copyright holder retains all rights, title, and interest
in and to the Software, including all intellectual property rights.
4. TERMINATION
This license is effective until terminated. It will terminate automatically
without notice if you fail to comply with any provision of this license.
5. DISCLAIMER OF WARRANTIES
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
6. LIMITATION OF LIABILITY
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
For licensing inquiries, please contact: https://mchus.pro

View File

@@ -112,8 +112,8 @@ func (e *Exporter) ExportJSON(w io.Writer) error {
// ExportTXT exports a human-readable text report
func (e *Exporter) ExportTXT(w io.Writer) error {
fmt.Fprintln(w, "LOGPile Analysis Report")
fmt.Fprintln(w, "========================")
fmt.Fprintln(w, "LOGPile Analysis Report - mchus.pro")
fmt.Fprintln(w, "====================================")
fmt.Fprintln(w)
if e.result == nil {
@@ -254,5 +254,11 @@ func (e *Exporter) ExportTXT(w io.Writer) error {
fmt.Fprintf(w, " Warning: %d\n", warning)
fmt.Fprintf(w, " Info: %d\n", info)
// Footer
fmt.Fprintln(w)
fmt.Fprintln(w, "------------------------------------")
fmt.Fprintln(w, "Generated by LOGPile - mchus.pro")
fmt.Fprintln(w, "https://git.mchus.pro/mchus/logpile")
return nil
}

View File

@@ -353,6 +353,15 @@ func (s *Server) handleGetSerials(w http.ResponseWriter, r *http.Request) {
Category: "PSU",
})
}
// Firmware (using version as "serial number" for display)
for _, fw := range result.Hardware.Firmware {
serials = append(serials, SerialEntry{
Component: fw.DeviceName,
SerialNumber: fw.Version,
Category: "Firmware",
})
}
}
jsonResponse(w, serials)
@@ -364,7 +373,81 @@ func (s *Server) handleGetFirmware(w http.ResponseWriter, r *http.Request) {
jsonResponse(w, []interface{}{})
return
}
jsonResponse(w, result.Hardware.Firmware)
// Deduplicate firmware by extracting model name and version
// E.g., "PSU0 (AP-CR3000F12BY)" and "PSU1 (AP-CR3000F12BY)" with same version -> one entry
type FirmwareEntry struct {
Component string `json:"component"`
Model string `json:"model"`
Version string `json:"version"`
}
seen := make(map[string]bool)
var deduplicated []FirmwareEntry
for _, fw := range result.Hardware.Firmware {
// Extract component type and model from device name
component, model := extractFirmwareComponentAndModel(fw.DeviceName)
key := component + "|" + model + "|" + fw.Version
if !seen[key] {
seen[key] = true
deduplicated = append(deduplicated, FirmwareEntry{
Component: component,
Model: model,
Version: fw.Version,
})
}
}
jsonResponse(w, deduplicated)
}
// extractFirmwareComponentAndModel extracts the component type and model from firmware device name
func extractFirmwareComponentAndModel(deviceName string) (component, model string) {
// Parse different firmware name formats and extract component + model
// For "PSU0 (AP-CR3000F12BY)" -> component: "PSU", model: "AP-CR3000F12BY"
if strings.HasPrefix(deviceName, "PSU") {
if idx := strings.Index(deviceName, "("); idx != -1 {
model = strings.Trim(deviceName[idx:], "()")
return "PSU", model
}
return "PSU", "-"
}
// For "CPU0 Microcode" -> component: "CPU Microcode", model: "-"
if strings.HasPrefix(deviceName, "CPU") && strings.Contains(deviceName, "Microcode") {
return "CPU Microcode", "-"
}
// For "NIC #CPU1_PCIE9 (MCX512A-ACAT)" -> component: "NIC", model: "MCX512A-ACAT"
if strings.HasPrefix(deviceName, "NIC ") {
if idx := strings.Index(deviceName, "("); idx != -1 {
model = strings.Trim(deviceName[idx:], "()")
return "NIC", model
}
return "NIC", "-"
}
// For "HDD Samsung MZ7L33T8HBNA-00A07" -> component: "HDD", model: "Samsung MZ7L33T8HBNA-00A07"
if strings.HasPrefix(deviceName, "HDD ") {
return "HDD", strings.TrimPrefix(deviceName, "HDD ")
}
// For "SSD Samsung MZ7..." -> component: "SSD", model: "Samsung MZ7..."
if strings.HasPrefix(deviceName, "SSD ") {
return "SSD", strings.TrimPrefix(deviceName, "SSD ")
}
// For "NVMe KIOXIA..." -> component: "NVMe", model: "KIOXIA..."
if strings.HasPrefix(deviceName, "NVMe ") {
return "NVMe", strings.TrimPrefix(deviceName, "NVMe ")
}
// For simple names like "BIOS", "ME", "BKC", "Virtual MicroCo"
// component = name, model = "-"
return deviceName, "-"
}
func (s *Server) handleGetStatus(w http.ResponseWriter, r *http.Request) {

View File

@@ -27,6 +27,12 @@ header p {
opacity: 0.8;
}
.header-domain {
font-size: 0.9rem;
font-weight: 400;
opacity: 0.7;
}
main {
max-width: 1400px;
margin: 2rem auto;
@@ -248,6 +254,10 @@ code {
background: #9b59b6;
}
.category-badge.firmware {
background: #34495e;
}
/* Toolbar label */
.toolbar-label {
font-size: 0.875rem;
@@ -365,6 +375,21 @@ footer {
padding: 2rem;
}
.footer-info {
margin-top: 1rem;
font-size: 0.8rem;
color: #666;
}
.footer-info a {
color: #3498db;
text-decoration: none;
}
.footer-info a:hover {
text-decoration: underline;
}
#clear-btn {
background: #e74c3c;
color: white;

View File

@@ -155,6 +155,7 @@ function renderConfig(data) {
<button class="config-tab" data-config-tab="gpu">GPU</button>
<button class="config-tab" data-config-tab="network">Network</button>
<button class="config-tab" data-config-tab="pcie">Device Inventory</button>
<button class="config-tab" data-config-tab="fw">Firmware</button>
</div>`;
// Specification tab
@@ -340,6 +341,9 @@ function renderConfig(data) {
}
html += '</div>';
// Firmware tab (content will be populated after firmware loads)
html += '<div class="config-tab-content" id="config-fw"><div id="config-fw-content"><p class="no-data">Загрузка...</p></div></div>';
container.innerHTML = html;
// Initialize config sub-tabs
@@ -368,24 +372,47 @@ async function loadFirmware() {
}
}
let allFirmware = [];
function renderFirmware(firmware) {
allFirmware = firmware || [];
// Render in Firmware tab
const tbody = document.querySelector('#firmware-table tbody');
tbody.innerHTML = '';
if (!firmware || firmware.length === 0) {
tbody.innerHTML = '<tr><td colspan="3" class="no-data">Нет данных о прошивках</td></tr>';
return;
} else {
firmware.forEach(fw => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${escapeHtml(fw.component)}</td>
<td>${escapeHtml(fw.model)}</td>
<td><code>${escapeHtml(fw.version)}</code></td>
`;
tbody.appendChild(row);
});
}
firmware.forEach(fw => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${escapeHtml(fw.device_name)}</td>
<td><code>${escapeHtml(fw.version)}</code></td>
<td>${escapeHtml(fw.build_date || '-')}</td>
`;
tbody.appendChild(row);
});
// Render in Config -> Firmware tab
const configFwContent = document.getElementById('config-fw-content');
if (configFwContent) {
if (!firmware || firmware.length === 0) {
configFwContent.innerHTML = '<p class="no-data">Нет данных о прошивках</p>';
} else {
let html = '<h3>Прошивки компонентов</h3><table class="config-table"><thead><tr><th>Компонент</th><th>Модель</th><th>Версия</th></tr></thead><tbody>';
firmware.forEach(fw => {
html += `<tr>
<td>${escapeHtml(fw.component)}</td>
<td>${escapeHtml(fw.model)}</td>
<td><code>${escapeHtml(fw.version)}</code></td>
</tr>`;
});
html += '</tbody></table>';
configFwContent.innerHTML = html;
}
}
}
async function loadSensors() {
@@ -491,6 +518,7 @@ function renderSerials(serials) {
'PCIe': 'PCIe',
'Network': 'Сеть',
'PSU': 'БП',
'Firmware': 'Прошивка',
'FRU': 'FRU'
};

View File

@@ -8,7 +8,7 @@
</head>
<body>
<header>
<h1>LOGPile</h1>
<h1>LOGPile <span class="header-domain">mchus.pro</span></h1>
<p>Анализатор диагностических данных BMC/IPMI</p>
</header>
@@ -48,8 +48,8 @@
<thead>
<tr>
<th>Компонент</th>
<th>Модель</th>
<th>Версия</th>
<th>Дата сборки</th>
</tr>
</thead>
<tbody></tbody>
@@ -80,6 +80,7 @@
<option value="PCIe">PCIe устройства</option>
<option value="Network">Сетевые адаптеры</option>
<option value="PSU">Блоки питания</option>
<option value="Firmware">Прошивки</option>
<option value="FRU">FRU</option>
</select>
<button onclick="exportData('csv')">Экспорт CSV</button>
@@ -124,6 +125,9 @@
<footer>
<button id="clear-btn" class="hidden" onclick="clearData()">Очистить данные</button>
<div class="footer-info">
<p>Автор: <a href="https://mchus.pro" target="_blank">mchus.pro</a> | <a href="https://git.mchus.pro/mchus/logpile" target="_blank">Git Repository</a></p>
</div>
</footer>
<script src="/static/js/app.js"></script>