Files
2026-04-06 00:57:13 +02:00

141 lines
3.7 KiB
Go

package main
import (
"context"
"database/sql"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"sync"
)
// App struct — each public method is an IPC binding to the frontend.
type App struct {
ctx context.Context
config *DashboardConfig
engine *QueryEngine
pool map[string]*sql.DB
dashboardDir string // directory to scan for YAML dashboards
currentFile string // currently loaded YAML file
mu sync.RWMutex
}
func NewApp(cfg *DashboardConfig, engine *QueryEngine, pool map[string]*sql.DB, dashDir, currentFile string) *App {
return &App{
config: cfg,
engine: engine,
pool: pool,
dashboardDir: dashDir,
currentFile: currentFile,
}
}
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
// GetDashboardConfig returns the full config for the frontend to render.
func (a *App) GetDashboardConfig() *DashboardConfig {
a.mu.RLock()
defer a.mu.RUnlock()
log.Printf("[IPC] GetDashboardConfig — title=%q sections=%d", a.config.Settings.Title, len(a.config.Sections))
return a.config
}
// GetWidgetData executes the query for a widget with current filter values.
func (a *App) GetWidgetData(widgetID string, filters map[string]any) ([]map[string]any, error) {
a.mu.RLock()
defer a.mu.RUnlock()
rows, err := a.engine.Execute(widgetID, filters)
if err != nil {
log.Printf("[IPC] GetWidgetData ERROR — widget=%q err=%v", widgetID, err)
return nil, fmt.Errorf("widget %q: %w", widgetID, err)
}
log.Printf("[IPC] GetWidgetData OK — widget=%q rows=%d", widgetID, len(rows))
return rows, nil
}
// DashboardInfo is a summary of an available dashboard file.
type DashboardInfo struct {
Name string `json:"name"`
File string `json:"file"`
Title string `json:"title"`
Theme string `json:"theme"`
Current bool `json:"current"`
}
// ListDashboards returns all .yaml files in the dashboards directory.
func (a *App) ListDashboards() []DashboardInfo {
a.mu.RLock()
currentFile := a.currentFile
a.mu.RUnlock()
var dashboards []DashboardInfo
entries, err := os.ReadDir(a.dashboardDir)
if err != nil {
log.Printf("[IPC] ListDashboards ERROR — %v", err)
return dashboards
}
for _, e := range entries {
if e.IsDir() || (!strings.HasSuffix(e.Name(), ".yaml") && !strings.HasSuffix(e.Name(), ".yml")) {
continue
}
filePath := filepath.Join(a.dashboardDir, e.Name())
cfg, err := LoadDashboard(filePath)
title := e.Name()
theme := ""
if err == nil {
title = cfg.Settings.Title
theme = cfg.Theme
}
dashboards = append(dashboards, DashboardInfo{
Name: strings.TrimSuffix(strings.TrimSuffix(e.Name(), ".yaml"), ".yml"),
File: e.Name(),
Title: title,
Theme: theme,
Current: filePath == currentFile || e.Name() == filepath.Base(currentFile),
})
}
log.Printf("[IPC] ListDashboards — found %d", len(dashboards))
return dashboards
}
// SwitchDashboard loads a different dashboard YAML by filename.
func (a *App) SwitchDashboard(fileName string) (*DashboardConfig, error) {
filePath := filepath.Join(a.dashboardDir, fileName)
log.Printf("[IPC] SwitchDashboard — loading %q", filePath)
cfg, err := LoadDashboard(filePath)
if err != nil {
return nil, fmt.Errorf("loading %q: %w", fileName, err)
}
newPool, err := OpenConnections(cfg.Connections, filepath.Dir(filePath))
if err != nil {
return nil, fmt.Errorf("connections for %q: %w", fileName, err)
}
a.mu.Lock()
// Close old connections
if a.pool != nil {
CloseConnections(a.pool)
}
a.config = cfg
a.pool = newPool
a.engine = NewQueryEngine(cfg, newPool)
a.currentFile = filePath
a.mu.Unlock()
log.Printf("[IPC] SwitchDashboard OK — title=%q sections=%d", cfg.Settings.Title, len(cfg.Sections))
return cfg, nil
}