diff --git a/main.go b/main.go index 19c1eca..45b56e3 100644 --- a/main.go +++ b/main.go @@ -792,12 +792,13 @@ type mqttManager struct { // --------------------------------------------------------------------------- var ( - cfg Config - cfgMu sync.RWMutex - state AppState - db *sql.DB - sampleCh chan Sample - alarmCh chan AlarmEvent + cfg Config + cfgMu sync.RWMutex + state AppState + db *sql.DB + sampleCh chan Sample + alarmCh chan AlarmEvent + indexTmpl *template.Template alarmTracker AlarmTracker uiRevision uint64 = 1 @@ -2830,11 +2831,12 @@ func queryReportSummary(ctx context.Context, window time.Duration, label string) if err != nil { return ReportSummaryResponse{}, err } + defer alarmRows.Close() + for alarmRows.Next() { var tsUnix int64 var severity, source, code string if err := alarmRows.Scan(&tsUnix, &severity, &source, &code); err != nil { - alarmRows.Close() return ReportSummaryResponse{}, err } labelKey := reportBucketLabel(time.Unix(0, tsUnix), window) @@ -2848,7 +2850,9 @@ func queryReportSummary(ctx context.Context, window time.Duration, label string) plcDiscByBucket[labelKey]++ } } - alarmRows.Close() + if err := alarmRows.Err(); err != nil { + return ReportSummaryResponse{}, err + } buckets := make([]ReportBucket, 0, len(order)) for _, key := range order { @@ -3300,9 +3304,8 @@ async function activate(){ } // License OK — serve the full dashboard template from the embedded static files - tmpl, err := template.ParseFS(embeddedStaticFiles, "static/index.html") - if err != nil { - log.Printf("template parse error: %v", err) + if indexTmpl == nil { + log.Printf("dashboard template not initialized") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } @@ -3349,7 +3352,7 @@ async function activate(){ w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Cache-Control", "no-store") - if err := tmpl.Execute(w, data); err != nil { + if err := indexTmpl.Execute(w, data); err != nil { log.Printf("template execute error: %v", err) } return @@ -3622,6 +3625,11 @@ func main() { log.Fatalf("invalid config: %v", err) } + indexTmpl, err = template.ParseFS(embeddedStaticFiles, "static/index.html") + if err != nil { + log.Fatalf("failed to parse embedded dashboard template: %v", err) + } + dbPath := cfg.DB.Path if !filepath.IsAbs(dbPath) { dbPath = filepath.Join(wd, dbPath) @@ -3742,11 +3750,12 @@ func main() { mux.HandleFunc("/api/license/activate", apiLicenseActivate) srv := &http.Server{ - Addr: cfg.Server.ListenAddr, - Handler: mux, - ReadTimeout: 15 * time.Second, - WriteTimeout: 15 * time.Second, - IdleTimeout: 60 * time.Second, + Addr: cfg.Server.ListenAddr, + Handler: mux, + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 15 * time.Second, + WriteTimeout: 15 * time.Second, + IdleTimeout: 60 * time.Second, } log.Printf("Listening address: %s", cfg.Server.ListenAddr) diff --git a/static/history.html b/static/history.html index 5a223e4..39bf114 100644 --- a/static/history.html +++ b/static/history.html @@ -514,7 +514,7 @@ function wireEvents() { AppUI.initTheme({ onChange: (t) => { currentTheme = t; updateChartTheme(); } }); AppUI.initFullscreen({ buttonId:'fullscreen-toggle' }); - updateFullscreenButton(); + AppUI.updateFullscreenButton('fullscreen-toggle'); qs('refresh-btn').addEventListener('click', refreshAll); qs('export-csv').addEventListener('click', exportCsv); qs('apply-window').addEventListener('click', () => { @@ -543,4 +543,4 @@ })(); - + \ No newline at end of file diff --git a/static/kiosk.html b/static/kiosk.html index b485ff4..36a2685 100644 --- a/static/kiosk.html +++ b/static/kiosk.html @@ -6,9 +6,12 @@ Force Monitor — Kiosk