mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-14 02:37:29 +08:00
108 lines
2.8 KiB
Go
108 lines
2.8 KiB
Go
package nodes
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type RegistryServer struct {
|
|
addr string
|
|
token string
|
|
mgr *Manager
|
|
server *http.Server
|
|
}
|
|
|
|
func NewRegistryServer(host string, port int, token string, mgr *Manager) *RegistryServer {
|
|
addr := strings.TrimSpace(host)
|
|
if addr == "" {
|
|
addr = "0.0.0.0"
|
|
}
|
|
if port <= 0 {
|
|
port = 7788
|
|
}
|
|
return &RegistryServer{addr: fmt.Sprintf("%s:%d", addr, port), token: strings.TrimSpace(token), mgr: mgr}
|
|
}
|
|
|
|
func (s *RegistryServer) Start(ctx context.Context) error {
|
|
if s.mgr == nil {
|
|
return nil
|
|
}
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte("ok"))
|
|
})
|
|
mux.HandleFunc("/nodes/register", s.handleRegister)
|
|
mux.HandleFunc("/nodes/heartbeat", s.handleHeartbeat)
|
|
s.server = &http.Server{Addr: s.addr, Handler: mux}
|
|
go func() {
|
|
<-ctx.Done()
|
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
_ = s.server.Shutdown(shutdownCtx)
|
|
}()
|
|
go func() { _ = s.server.ListenAndServe() }()
|
|
return nil
|
|
}
|
|
|
|
func (s *RegistryServer) handleRegister(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
if !s.checkAuth(r) {
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
var n NodeInfo
|
|
if err := json.NewDecoder(r.Body).Decode(&n); err != nil {
|
|
http.Error(w, "invalid json", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if strings.TrimSpace(n.ID) == "" {
|
|
http.Error(w, "id required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
s.mgr.Upsert(n)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "id": n.ID})
|
|
}
|
|
|
|
func (s *RegistryServer) handleHeartbeat(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
if !s.checkAuth(r) {
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
var body struct{ ID string `json:"id"` }
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil || strings.TrimSpace(body.ID) == "" {
|
|
http.Error(w, "id required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
n, ok := s.mgr.Get(body.ID)
|
|
if !ok {
|
|
http.Error(w, "node not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
n.LastSeenAt = time.Now().UTC()
|
|
n.Online = true
|
|
s.mgr.Upsert(n)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "id": body.ID})
|
|
}
|
|
|
|
func (s *RegistryServer) checkAuth(r *http.Request) bool {
|
|
if s.token == "" {
|
|
return true
|
|
}
|
|
auth := strings.TrimSpace(r.Header.Get("Authorization"))
|
|
return auth == "Bearer "+s.token
|
|
}
|