146 lines
4.8 KiB
Go
146 lines
4.8 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"cloudipvalidator/internal/db"
|
|
)
|
|
|
|
func (s *Server) handleAgentRegister(w http.ResponseWriter, r *http.Request) {
|
|
var req registerAgentRequest
|
|
if err := readJSON(r, &req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
|
|
return
|
|
}
|
|
if req.ValidatorID == "" {
|
|
writeError(w, http.StatusBadRequest, "validator_id is required")
|
|
return
|
|
}
|
|
// os_port_id is supplied via control-api's own config (config.ValidatorConfig),
|
|
// not by the agent, so registration only touches hostname/version here;
|
|
// RegisterValidator preserves any existing os_port_id row.
|
|
existing, _ := s.DB.GetValidator(r.Context(), req.ValidatorID)
|
|
osPortID := ""
|
|
if existing != nil {
|
|
osPortID = existing.OSPortID
|
|
}
|
|
if err := s.DB.RegisterValidator(r.Context(), req.ValidatorID, req.Hostname, osPortID, req.AgentVersion); err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
s.Orch.RecordEvent(r.Context(), "validator-agent", req.ValidatorID, nil, "registered", "")
|
|
writeJSON(w, http.StatusOK, registerAgentResponse{OK: true, PollIntervalSeconds: s.Orch.Cfg.PollIntervalSeconds})
|
|
}
|
|
|
|
func (s *Server) handleAgentHeartbeat(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
var req heartbeatRequest
|
|
_ = readJSON(r, &req) // heartbeat body is informational only; tolerate empty/missing
|
|
if err := s.DB.Heartbeat(r.Context(), id); err != nil {
|
|
writeError(w, http.StatusNotFound, "unknown validator: "+id)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, okResponse{OK: true})
|
|
}
|
|
|
|
func (s *Server) handleAgentAssignment(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
item, checks, err := s.Orch.AssignmentForValidator(r.Context(), id)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, "unknown validator: "+id)
|
|
return
|
|
}
|
|
if item == nil {
|
|
w.WriteHeader(http.StatusNoContent)
|
|
return
|
|
}
|
|
var cfg []checkConfigDTO
|
|
for _, c := range checks {
|
|
cfg = append(cfg, checkConfigDTO{Type: c.Type, Targets: c.Targets})
|
|
}
|
|
writeJSON(w, http.StatusOK, assignmentResponse{
|
|
IPID: item.ID, IPAddress: item.IPAddress, Phase: item.State, CheckConfig: cfg,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleAgentSelfCheck(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
var req selfCheckRequest
|
|
if err := readJSON(r, &req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
|
|
return
|
|
}
|
|
detail := req.Detail
|
|
if req.DetectedEgress != "" {
|
|
detail = "detected_egress_ip=" + req.DetectedEgress + " " + detail
|
|
}
|
|
if err := s.Orch.SelfCheckResult(r.Context(), id, req.IPID, req.Success, detail); err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, okResponse{OK: true})
|
|
}
|
|
|
|
func (s *Server) handleAgentEvent(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
var req agentEventRequest
|
|
if err := readJSON(r, &req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
|
|
return
|
|
}
|
|
if req.EventType == "" {
|
|
writeError(w, http.StatusBadRequest, "event_type is required")
|
|
return
|
|
}
|
|
s.Orch.RecordEvent(r.Context(), "validator-agent", id, req.IPID, req.EventType, req.Payload)
|
|
writeJSON(w, http.StatusOK, okResponse{OK: true})
|
|
}
|
|
|
|
func (s *Server) handleAgentResults(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
var req agentResultsRequest
|
|
if err := readJSON(r, &req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
|
|
return
|
|
}
|
|
for _, res := range req.Results {
|
|
item, err := s.DB.GetIP(r.Context(), res.IPID)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, "unknown ip_id")
|
|
return
|
|
}
|
|
checkedAt, err := time.Parse(time.RFC3339Nano, res.CheckedAt)
|
|
if err != nil {
|
|
checkedAt = db.Now()
|
|
}
|
|
err = s.Orch.RecordCheck(r.Context(), db.Check{
|
|
IPID: item.ID, IPAddress: item.IPAddress, AttemptNumber: item.AttemptNumber,
|
|
ValidatorID: id, Source: db.SourceEgress, CheckType: res.CheckType, Target: res.Target,
|
|
Success: res.Success, LatencyMS: res.LatencyMS, Detail: res.Detail, CheckedAt: checkedAt,
|
|
})
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
}
|
|
writeJSON(w, http.StatusOK, okResponse{OK: true})
|
|
}
|
|
|
|
func (s *Server) handleAgentComplete(w http.ResponseWriter, r *http.Request) {
|
|
var req agentCompleteRequest
|
|
if err := readJSON(r, &req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
|
|
return
|
|
}
|
|
if err := s.Orch.MarkEgressComplete(r.Context(), req.IPID); err != nil {
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, okResponse{OK: true})
|
|
}
|
|
|
|
func (s *Server) handleWhatsMyIP(w http.ResponseWriter, r *http.Request) {
|
|
writeJSON(w, http.StatusOK, map[string]string{"ip": remoteIP(r)})
|
|
}
|