Wire up events for creating new public reports
This involved moving a lot of stuff to the platform layer since I don't want event interfaces leaking out. Also this includes a fix to the user authentication which I had previously broken by making a platform-layer user object independent of the database layer.
This commit is contained in:
parent
9a5cc4cf97
commit
e8d865d0ab
24 changed files with 915 additions and 541 deletions
138
api/event.go
138
api/event.go
|
|
@ -7,77 +7,45 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var connectionsSSE map[*ConnectionSSE]bool = make(map[*ConnectionSSE]bool, 0)
|
||||
|
||||
func streamEvents(w http.ResponseWriter, r *http.Request, u platform.User) {
|
||||
// Set headers for SSE
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
connection := ConnectionSSE{
|
||||
chanState: make(chan MessageSSE),
|
||||
id: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
}
|
||||
connectionsSSE[&connection] = true
|
||||
// Send an initial connected event
|
||||
fmt.Fprintf(w, "event: connected\ndata: {\"status\": \"connected\", \"time\": \"%s\"}\n\n", time.Now().Format(time.RFC3339))
|
||||
w.(http.Flusher).Flush()
|
||||
|
||||
// Keep the connection open with a ticker sending periodic events
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
// Use a channel to detect when the client disconnects
|
||||
done := r.Context().Done()
|
||||
|
||||
// Keep connection open until client disconnects
|
||||
var err error
|
||||
for {
|
||||
err = nil
|
||||
select {
|
||||
case <-done:
|
||||
log.Info().Msg("Client closed connection")
|
||||
return
|
||||
case t := <-ticker.C:
|
||||
// Send a heartbeat message
|
||||
err = connection.SendHeartbeat(w, t)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send state from webserver")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MessageHeartbeat struct {
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
type MessageSSE struct {
|
||||
Content any `json:"content"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
type ConnectionSSE struct {
|
||||
chanState chan MessageSSE
|
||||
id string
|
||||
chanEvent chan platform.Event
|
||||
id uuid.UUID
|
||||
organizationID int32
|
||||
userID int
|
||||
}
|
||||
|
||||
func (c *ConnectionSSE) SendMessage(w http.ResponseWriter, m MessageSSE) error {
|
||||
return send(w, MessageSSE{
|
||||
Type: "heartbeat",
|
||||
})
|
||||
func (c *ConnectionSSE) SendEvent(w http.ResponseWriter, m platform.Event) error {
|
||||
return send(w, m)
|
||||
}
|
||||
func (c *ConnectionSSE) SendHeartbeat(w http.ResponseWriter, t time.Time) error {
|
||||
return send(w, MessageSSE{
|
||||
Content: MessageHeartbeat{
|
||||
Time: t,
|
||||
},
|
||||
Type: "heartbeat",
|
||||
return send(w, platform.Event{
|
||||
Resource: "clock",
|
||||
Time: t,
|
||||
Type: platform.EventTypeHeartbeat,
|
||||
URI: "",
|
||||
})
|
||||
}
|
||||
func SetEventChannel(chan_envelopes <-chan platform.Envelope) {
|
||||
go func() {
|
||||
for envelope := range chan_envelopes {
|
||||
for conn, _ := range connectionsSSE {
|
||||
if conn.organizationID == envelope.OrganizationID {
|
||||
log.Debug().Int("type", int(envelope.Event.Type)).Int32("env-org", envelope.OrganizationID).Msg("pushed event to client")
|
||||
conn.chanEvent <- envelope.Event
|
||||
} else {
|
||||
log.Debug().Int("type", int(envelope.Event.Type)).Int32("env-org", envelope.OrganizationID).Int32("conn-org", conn.organizationID).Msg("skipped event, bad org")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
func send[T any](w http.ResponseWriter, msg T) error {
|
||||
jsonData, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
|
|
@ -92,3 +60,55 @@ func send[T any](w http.ResponseWriter, msg T) error {
|
|||
w.(http.Flusher).Flush()
|
||||
return nil
|
||||
}
|
||||
func streamEvents(w http.ResponseWriter, r *http.Request, u platform.User) {
|
||||
// Set headers for SSE
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
uid, err := uuid.NewUUID()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to create uuid")
|
||||
}
|
||||
connection := ConnectionSSE{
|
||||
chanEvent: make(chan platform.Event),
|
||||
id: uid,
|
||||
organizationID: u.Organization.ID(),
|
||||
userID: u.ID,
|
||||
}
|
||||
connectionsSSE[&connection] = true
|
||||
log.Debug().Int32("org", u.Organization.ID()).Int("user", u.ID).Str("id", uid.String()).Msg("connected SSE client")
|
||||
|
||||
// Send an initial connected event
|
||||
fmt.Fprintf(w, "event: connected\ndata: {\"status\": \"connected\", \"time\": \"%s\"}\n\n", time.Now().Format(time.RFC3339))
|
||||
w.(http.Flusher).Flush()
|
||||
|
||||
// Keep the connection open with a ticker sending periodic events
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
// Use a channel to detect when the client disconnects
|
||||
done := r.Context().Done()
|
||||
|
||||
// Keep connection open until client disconnects
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
log.Debug().Int32("org", u.Organization.ID()).Int("user", u.ID).Str("id", uid.String()).Msg("Client closed connection")
|
||||
delete(connectionsSSE, &connection)
|
||||
return
|
||||
case t := <-ticker.C:
|
||||
// Send a heartbeat message
|
||||
err = connection.SendHeartbeat(w, t)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send heartbeat")
|
||||
}
|
||||
case e := <-connection.chanEvent:
|
||||
err = connection.SendEvent(w, e)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send heartbeat")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue