Add M0 server skeleton with SQLite store, SSE events, worktrees, and tailscale service
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"g.fc.run/misc/byre/internal/api"
|
||||
)
|
||||
|
||||
const backfillPage = 500
|
||||
|
||||
func (s *Server) StreamEvents(w http.ResponseWriter, r *http.Request, params api.StreamEventsParams) {
|
||||
last := int64(0)
|
||||
if params.After != nil {
|
||||
last = *params.After
|
||||
}
|
||||
if params.LastEventID != nil {
|
||||
last = *params.LastEventID
|
||||
}
|
||||
|
||||
rc := http.NewResponseController(w)
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
live := s.hub.subscribe()
|
||||
defer s.hub.unsubscribe(live)
|
||||
|
||||
for {
|
||||
events, err := s.store.EventsAfter(r.Context(), last, backfillPage)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, ev := range events {
|
||||
if !writeEvent(w, ev) {
|
||||
return
|
||||
}
|
||||
last = ev.Seq
|
||||
}
|
||||
if len(events) < backfillPage {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if rc.Flush() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ping := time.NewTicker(30 * time.Second)
|
||||
defer ping.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case ev, ok := <-live:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if ev.Seq <= last {
|
||||
continue
|
||||
}
|
||||
if !writeEvent(w, ev) {
|
||||
return
|
||||
}
|
||||
last = ev.Seq
|
||||
|
||||
case <-ping.C:
|
||||
_, err := fmt.Fprint(w, ": ping\n\n")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
case <-r.Context().Done():
|
||||
return
|
||||
}
|
||||
|
||||
if rc.Flush() != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeEvent(w http.ResponseWriter, ev api.Event) bool {
|
||||
data, err := json.Marshal(ev)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = fmt.Fprintf(w, "id: %d\ndata: %s\n\n", ev.Seq, data)
|
||||
return err == nil
|
||||
}
|
||||
Reference in New Issue
Block a user