83 lines
1.4 KiB
Go
83 lines
1.4 KiB
Go
|
|
package tools
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"encoding/json"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"net/http"
|
||
|
|
"sync"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
var (
|
||
|
|
ErrEventsUnsupported = errors.New("events unsupported")
|
||
|
|
)
|
||
|
|
|
||
|
|
type EventHelper struct {
|
||
|
|
m sync.Mutex
|
||
|
|
c context.Context
|
||
|
|
w http.ResponseWriter
|
||
|
|
r *http.Request
|
||
|
|
f http.Flusher
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewEventHelper(ctx context.Context, w http.ResponseWriter, r *http.Request) (*EventHelper, error) {
|
||
|
|
|
||
|
|
// Setup Connection
|
||
|
|
flusher, ok := w.(http.Flusher)
|
||
|
|
if !ok {
|
||
|
|
return nil, ErrEventsUnsupported
|
||
|
|
}
|
||
|
|
|
||
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
||
|
|
w.Header().Set("Cache-Control", "no-cache")
|
||
|
|
w.Header().Set("Connection", "keep-alive")
|
||
|
|
|
||
|
|
sse := &EventHelper{f: flusher, c: ctx, w: w, r: r}
|
||
|
|
|
||
|
|
// Heartbeat Generator
|
||
|
|
go func(h *EventHelper) {
|
||
|
|
t := time.NewTicker(5 * time.Second)
|
||
|
|
defer t.Stop()
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case <-h.c.Done():
|
||
|
|
return
|
||
|
|
case <-t.C:
|
||
|
|
h.m.Lock()
|
||
|
|
fmt.Fprintf(h.w, ": ping\n\n")
|
||
|
|
h.f.Flush()
|
||
|
|
h.m.Unlock()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}(sse)
|
||
|
|
|
||
|
|
return sse, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *EventHelper) SendJSON(eventName string, eventData any) {
|
||
|
|
|
||
|
|
b, err := json.Marshal(map[string]any{
|
||
|
|
"name": eventName,
|
||
|
|
"data": eventData,
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
h.m.Lock()
|
||
|
|
fmt.Fprintf(h.w, "data: %s\n\n", b)
|
||
|
|
h.f.Flush()
|
||
|
|
h.m.Unlock()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *EventHelper) SendServerError(err error) {
|
||
|
|
SendServerError(nil, h.r, err)
|
||
|
|
h.SendClientError(ERROR_GENERIC_SERVER)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *EventHelper) SendClientError(err APIError) {
|
||
|
|
h.SendJSON("error", err)
|
||
|
|
}
|