package beacon import ( "encoding/json" "net/http" "sync" "time" "git.send.nrw/sendnrw/decent-websrv/internal/mesh" ) type Server struct { addr string token string reg *registry http *http.Server } type registry struct { mu sync.Mutex m map[string]mesh.NodeInfo exp map[string]int64 // id->expires } func NewServer(addr, token string) *Server { s := &Server{addr: addr, token: token, reg: ®istry{m: map[string]mesh.NodeInfo{}, exp: map[string]int64{}}} mux := http.NewServeMux() mux.HandleFunc("/_beacon/register", s.handleRegister) mux.HandleFunc("/_beacon/peers", s.handlePeers) s.http = &http.Server{Addr: addr, Handler: mux, ReadHeaderTimeout: 5 * time.Second} return s } func (s *Server) ListenAndServe() error { return s.http.ListenAndServe() } func (s *Server) ListenAndServeTLS(cert, key string) error { return s.http.ListenAndServeTLS(cert, key) } func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) { if s.token != "" && r.Header.Get("X-Beacon-Token") != s.token { http.Error(w, "forbidden", 403) return } var in RegisterReq if err := json.NewDecoder(r.Body).Decode(&in); err != nil { http.Error(w, "bad", 400) return } n := mesh.NodeInfo{NodeID: in.NodeID, PublicURL: in.BaseURL, MeshURL: in.MeshURL} exp := time.Now().Add(time.Duration(in.TTL) * time.Second).Unix() s.reg.mu.Lock() s.reg.m[n.NodeID] = n s.reg.exp[n.NodeID] = exp s.reg.mu.Unlock() w.WriteHeader(200) } func (s *Server) handlePeers(w http.ResponseWriter, r *http.Request) { if s.token != "" && r.Header.Get("X-Beacon-Token") != s.token { http.Error(w, "forbidden", 403) return } now := time.Now().Unix() s.reg.mu.Lock() out := make([]mesh.NodeInfo, 0, len(s.reg.m)) for id, n := range s.reg.m { if s.reg.exp[id] > now { out = append(out, n) } } s.reg.mu.Unlock() _ = json.NewEncoder(w).Encode(PeersResp{Peers: out}) }