init: rapid_dashboards app from fn_registry
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user