diff --git a/.gitignore b/.gitignore index 09fd3de..8a4929c 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,6 @@ go.work.sum # env file .env + +# Distribution binaries +dist/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..422f395 --- /dev/null +++ b/LICENSE @@ -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 diff --git a/internal/exporter/exporter.go b/internal/exporter/exporter.go index 835b418..4a0f2b7 100644 --- a/internal/exporter/exporter.go +++ b/internal/exporter/exporter.go @@ -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 } diff --git a/internal/server/handlers.go b/internal/server/handlers.go index a5c764d..7287a43 100644 --- a/internal/server/handlers.go +++ b/internal/server/handlers.go @@ -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) { diff --git a/web/static/css/style.css b/web/static/css/style.css index bb648ad..5bb2a72 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -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; diff --git a/web/static/js/app.js b/web/static/js/app.js index 4d942fb..da03fa8 100644 --- a/web/static/js/app.js +++ b/web/static/js/app.js @@ -155,6 +155,7 @@ function renderConfig(data) { + `; // Specification tab @@ -340,6 +341,9 @@ function renderConfig(data) { } html += ''; + // Firmware tab (content will be populated after firmware loads) + html += '
Загрузка...
${escapeHtml(fw.version)}${escapeHtml(fw.version)}Нет данных о прошивках
'; + } else { + let html = '| Компонент | Модель | Версия |
|---|---|---|
| ${escapeHtml(fw.component)} | +${escapeHtml(fw.model)} | +${escapeHtml(fw.version)} |
+
Анализатор диагностических данных BMC/IPMI