Improving all side of app
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/uopi/uopi/internal/audit"
|
||||
@@ -34,11 +35,28 @@ type Engine struct {
|
||||
cancel context.CancelFunc // cancels the current generation
|
||||
wg *sync.WaitGroup // tracks the current generation's goroutines
|
||||
|
||||
// notifier delivers action.dialog requests to connected clients. Stored in
|
||||
// an atomic so flow goroutines can read it without taking e.mu (which Reload
|
||||
// holds while it waits for those same goroutines to drain).
|
||||
notifier atomic.Value // notifierBox
|
||||
|
||||
// Shared live signal cache for the current generation (key "ds\0name").
|
||||
liveMu sync.RWMutex
|
||||
live map[string]float64
|
||||
}
|
||||
|
||||
// notifierBox wraps a Notifier so atomic.Value always sees one concrete type.
|
||||
type notifierBox struct{ n Notifier }
|
||||
|
||||
// dialogSeq generates unique action.dialog ids across all graphs.
|
||||
var dialogSeq uint64
|
||||
|
||||
// SetNotifier installs the sink for action.dialog requests. Safe to call once
|
||||
// at startup before or after Reload; it is read lock-free by running flows.
|
||||
func (e *Engine) SetNotifier(n Notifier) {
|
||||
e.notifier.Store(notifierBox{n: n})
|
||||
}
|
||||
|
||||
// NewEngine creates an engine bound to root. Call Reload to start it. rec records
|
||||
// the writes performed by flows; pass audit.Nop() to disable auditing.
|
||||
func NewEngine(root context.Context, brk *broker.Broker, store *Store, rec audit.Recorder, log *slog.Logger) *Engine {
|
||||
@@ -252,6 +270,29 @@ func (e *Engine) write(cg *compiledGraph, target string, val float64) {
|
||||
e.audit.Record(ev)
|
||||
}
|
||||
|
||||
// emitDialog delivers an action.dialog node's request to the installed Notifier
|
||||
// (the WebSocket dialog hub). It is lock-free so it never deadlocks against a
|
||||
// concurrent Reload that is waiting for this flow goroutine to finish.
|
||||
func (e *Engine) emitDialog(n Node) {
|
||||
box, _ := e.notifier.Load().(notifierBox)
|
||||
if box.n == nil {
|
||||
return
|
||||
}
|
||||
kind := strings.TrimSpace(n.param("kind"))
|
||||
if kind != "error" && kind != "input" {
|
||||
kind = "info"
|
||||
}
|
||||
box.n.Notify(Dialog{
|
||||
ID: strconv.FormatUint(atomic.AddUint64(&dialogSeq, 1), 10),
|
||||
Kind: kind,
|
||||
Title: n.param("title"),
|
||||
Message: n.param("message"),
|
||||
Target: strings.TrimSpace(n.param("target")),
|
||||
Users: splitCSV(n.param("users")),
|
||||
Groups: splitCSV(n.param("groups")),
|
||||
})
|
||||
}
|
||||
|
||||
// ── compiled graph ─────────────────────────────────────────────────────────────
|
||||
|
||||
type wireOut struct {
|
||||
@@ -658,6 +699,10 @@ func (cg *compiledGraph) run(nodeID string, ctx *runCtx) {
|
||||
cg.runLua(node.ID, ctx)
|
||||
cg.follow(node.ID, "out", ctx)
|
||||
|
||||
case "action.dialog":
|
||||
cg.engine.emitDialog(node)
|
||||
cg.follow(node.ID, "out", ctx)
|
||||
|
||||
default:
|
||||
cg.follow(node.ID, "out", ctx)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user