feat(server): revision authors, revisions after a seq, online machines

This commit is contained in:
hh committed 2026-10-02 22:07:08 +00:00
1 parent 91d83f3032
commit c2e9284a63
6 files changed
+299 -15

No files matched your search

+37 -4
View File
@@ -50,12 +50,14 @@ type Server struct {
mu sync.Mutex
subscribers map[chan protocol.Event]struct{}
streams map[int64]int
seen map[int64]int64
}
type machineKey struct{}
func New(store *Store, config Config) http.Handler {
s := &Server{config: config, store: store, subscribers: map[chan protocol.Event]struct{}{}}
s := &Server{config: config, store: store, subscribers: map[chan protocol.Event]struct{}{}, streams: map[int64]int{}, seen: map[int64]int64{}}
mux := http.NewServeMux()
mux.HandleFunc("GET /i/{code}", s.install)
mux.Handle("GET /dl/", http.StripPrefix("/dl/", http.FileServer(http.Dir(config.ClientsDir))))
@@ -136,6 +138,7 @@ func (s *Server) auth(next http.HandlerFunc) http.HandlerFunc {
return
}
machineID = id
s.track(id, 0)
}
if key := r.PathValue("key"); key != "" && !repoKeyPattern.MatchString(key) {
http.Error(w, "bad repository key", http.StatusBadRequest)
@@ -206,12 +209,25 @@ func (s *Server) logout(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
func (s *Server) track(machineID int64, streams int) {
s.mu.Lock()
defer s.mu.Unlock()
s.seen[machineID] = time.Now().UnixMilli()
s.streams[machineID] += streams
}
func (s *Server) machines(w http.ResponseWriter, r *http.Request) {
machines, err := s.store.Machines(r.Context())
if err != nil {
writeError(w, err)
return
}
s.mu.Lock()
for i, machine := range machines {
machines[i].Online = s.streams[machine.ID] > 0
machines[i].LastSeen = max(s.seen[machine.ID], machine.LastPush)
}
s.mu.Unlock()
writeJSON(w, http.StatusOK, machines)
}
@@ -287,7 +303,19 @@ func (s *Server) state(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) revisions(w http.ResponseWriter, r *http.Request) {
revisions, err := s.store.Revisions(r.Context(), r.PathValue("key"), r.URL.Query().Get("path"))
key, query := r.PathValue("key"), r.URL.Query()
var revisions []protocol.Revision
var err error
if query.Has("after") {
after, parseErr := strconv.ParseInt(query.Get("after"), 10, 64)
if parseErr != nil {
http.Error(w, "bad after", http.StatusBadRequest)
return
}
revisions, err = s.store.RevisionsAfter(r.Context(), key, query.Get("path"), after)
} else {
revisions, err = s.store.Revisions(r.Context(), key, query.Get("path"))
}
if err != nil {
writeError(w, err)
return
@@ -319,8 +347,9 @@ func (s *Server) push(w http.ResponseWriter, r *http.Request) {
writeError(w, err)
return
}
s.publish(protocol.Event{Repo: key, Seq: seq})
writeJSON(w, http.StatusCreated, protocol.Event{Repo: key, Seq: seq})
event := protocol.Event{Repo: key, Seq: seq, Author: push.Author}
s.publish(event)
writeJSON(w, http.StatusCreated, event)
}
func (s *Server) blob(w http.ResponseWriter, r *http.Request) {
@@ -370,14 +399,17 @@ func (s *Server) events(w http.ResponseWriter, r *http.Request) {
http.Error(w, "streaming unsupported", http.StatusInternalServerError)
return
}
machineID, _ := r.Context().Value(machineKey{}).(int64)
subscriber := make(chan protocol.Event, 16)
s.mu.Lock()
s.subscribers[subscriber] = struct{}{}
s.mu.Unlock()
s.track(machineID, 1)
defer func() {
s.mu.Lock()
delete(s.subscribers, subscriber)
s.mu.Unlock()
s.track(machineID, -1)
}()
w.Header().Set("Content-Type", "text/event-stream")
@@ -391,6 +423,7 @@ func (s *Server) events(w http.ResponseWriter, r *http.Request) {
case <-r.Context().Done():
return
case <-ticker.C:
s.track(machineID, 0)
fmt.Fprint(w, ": ping\n\n")
case event := <-subscriber:
data, _ := json.Marshal(event)