ui: embed reanimator chart viewer

This commit is contained in:
Mikhail Chusavitin
2026-03-16 00:20:11 +03:00
parent f11a43f690
commit 057a222288
12 changed files with 361 additions and 167 deletions

View File

@@ -23,6 +23,7 @@ import (
"git.mchus.pro/mchus/logpile/internal/exporter"
"git.mchus.pro/mchus/logpile/internal/models"
"git.mchus.pro/mchus/logpile/internal/parser"
chartviewer "reanimator/chart/viewer"
)
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
@@ -47,6 +48,82 @@ func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
tmpl.Execute(w, nil)
}
func (s *Server) handleChartCurrent(w http.ResponseWriter, r *http.Request) {
result := s.GetResult()
title := chartTitle(result)
if result == nil || result.Hardware == nil {
html, err := chartviewer.RenderHTML(nil, title)
if err != nil {
http.Error(w, "failed to render viewer", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write(rewriteChartStaticPaths(html))
return
}
snapshotBytes, err := currentReanimatorSnapshotBytes(result)
if err != nil {
http.Error(w, "failed to build reanimator snapshot: "+err.Error(), http.StatusInternalServerError)
return
}
html, err := chartviewer.RenderHTML(snapshotBytes, title)
if err != nil {
http.Error(w, "failed to render chart: "+err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write(rewriteChartStaticPaths(html))
}
func currentReanimatorSnapshotBytes(result *models.AnalysisResult) ([]byte, error) {
reanimatorData, err := exporter.ConvertToReanimator(result)
if err != nil {
return nil, err
}
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
encoder.SetIndent("", " ")
if err := encoder.Encode(reanimatorData); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func chartTitle(result *models.AnalysisResult) string {
const fallback = "LOGPile Reanimator Viewer"
if result == nil {
return fallback
}
if result.Hardware != nil {
board := result.Hardware.BoardInfo
product := strings.TrimSpace(board.ProductName)
serial := strings.TrimSpace(board.SerialNumber)
switch {
case product != "" && serial != "":
return product + " - " + serial
case product != "":
return product
case serial != "":
return serial
}
}
if host := strings.TrimSpace(result.TargetHost); host != "" {
return host
}
if filename := strings.TrimSpace(result.Filename); filename != "" {
return filename
}
return fallback
}
func rewriteChartStaticPaths(html []byte) []byte {
return bytes.ReplaceAll(html, []byte(`href="/static/view.css"`), []byte(`href="/chart/static/view.css"`))
}
func (s *Server) handleUpload(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, uploadMultipartMaxBytes())
if err := r.ParseMultipartForm(uploadMultipartFormMemoryBytes()); err != nil {