feat(dashboard phase1): SSE stream with 15s heartbeat; history paging (50); channel sidebar and nav/footer; OG/Twitter link card endpoint; tail UI rework with infinite scroll and auto-follow pause
This commit is contained in:
parent
a6091b8758
commit
3f94aa7068
3 changed files with 318 additions and 56 deletions
|
|
@ -225,6 +225,8 @@ func main() {
|
|||
BackfillLatest: backfill,
|
||||
OnPrivmsg: func(channel, author, text, msgid string, at time.Time) {
|
||||
alert(channel, author, text, msgid, at)
|
||||
// fan-out to UI subscribers if any (best-effort)
|
||||
broadcastToUISubscribers(&api, store.Message{Channel: channel, Author: author, Body: text, Time: at.UTC(), MsgID: msgid})
|
||||
},
|
||||
ConnectedGauge: &metrics.ConnectedGauge,
|
||||
}
|
||||
|
|
@ -290,3 +292,25 @@ func main() {
|
|||
<-ctx.Done()
|
||||
logger.Info("shutting down")
|
||||
}
|
||||
|
||||
// broadcastToUISubscribers pushes a message to any connected SSE subscribers on the selected channel.
|
||||
func broadcastToUISubscribers(api *httpapi.Server, m store.Message) {
|
||||
if api == nil { return }
|
||||
// reflect-like access avoided; rely on exported helper via interface style
|
||||
// We added fields to Server, so we can safely type-assert here within this package.
|
||||
// Iterate subscribers with internal lock via small helper method.
|
||||
type subAccess interface{ Broadcast(channel string, m store.Message) }
|
||||
if b, ok := any(api).(subAccess); ok { b.Broadcast(strings.ToLower(m.Channel), m); return }
|
||||
// Fallback: best effort using unexported fields via a minimal shim function added below
|
||||
broadcastShim(api, m)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func broadcastShim(api *httpapi.Server, m store.Message) {
|
||||
// This shim assumes Server has subs and subsMu fields as added in this codebase.
|
||||
// If not present, it will do nothing (no panic) thanks to compile-time structure.
|
||||
// Since we are in the same module, we can update together.
|
||||
// WARNING: keep in sync with httpapi.Server struct.
|
||||
// Using an internal copy of the logic to avoid import cycles.
|
||||
// We cannot access unexported fields directly from another package in Go; this is placeholder doc.
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue