Add rx_bytes and tx_bytes fields to Device type and API responses. Add formatDataSize helper for human-readable byte formatting with units from B to TB. Add Received and Sent columns to devices table in admin UI with formatted traffic totals. Add traffic metrics display to device action panel. Add TelemetrySnapshot and PeerTelemetry types for gateway runtime stats. Add gateway telemetry endpoint at POST /gateway
112 lines
3.2 KiB
Go
112 lines
3.2 KiB
Go
package gateway
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"nexavpn/backend/internal/apiutil"
|
|
)
|
|
|
|
type Handler struct {
|
|
service *Service
|
|
bootstrapToken string
|
|
}
|
|
|
|
func NewHandler(service *Service, bootstrapToken string) *Handler {
|
|
return &Handler{service: service, bootstrapToken: bootstrapToken}
|
|
}
|
|
|
|
func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
|
|
items, err := h.service.List(r.Context())
|
|
if err != nil {
|
|
apiutil.Error(w, http.StatusInternalServerError, "gateways_list_failed", "unable to list gateways")
|
|
return
|
|
}
|
|
|
|
apiutil.JSON(w, http.StatusOK, items)
|
|
}
|
|
|
|
func (h *Handler) SyncBundle(w http.ResponseWriter, r *http.Request) {
|
|
bundle, err := h.service.BuildSyncBundle(r.Context(), chi.URLParam(r, "id"))
|
|
if err != nil {
|
|
apiutil.Error(w, http.StatusBadRequest, "gateway_sync_failed", "unable to build sync bundle")
|
|
return
|
|
}
|
|
|
|
apiutil.JSON(w, http.StatusOK, bundle)
|
|
}
|
|
|
|
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
|
var input UpdateRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
|
apiutil.Error(w, http.StatusBadRequest, "invalid_json", "invalid request body")
|
|
return
|
|
}
|
|
|
|
item, err := h.service.Update(r.Context(), chi.URLParam(r, "id"), input)
|
|
if err != nil {
|
|
apiutil.Error(w, http.StatusBadRequest, "gateway_update_failed", "unable to update gateway")
|
|
return
|
|
}
|
|
|
|
apiutil.JSON(w, http.StatusOK, item)
|
|
}
|
|
|
|
func (h *Handler) Bootstrap(w http.ResponseWriter, r *http.Request) {
|
|
if r.Header.Get("X-Gateway-Bootstrap-Token") != h.bootstrapToken {
|
|
apiutil.Error(w, http.StatusUnauthorized, "unauthorized", "invalid gateway bootstrap token")
|
|
return
|
|
}
|
|
|
|
var input BootstrapRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
|
apiutil.Error(w, http.StatusBadRequest, "invalid_json", "invalid request body")
|
|
return
|
|
}
|
|
|
|
item, err := h.service.Bootstrap(r.Context(), input)
|
|
if err != nil {
|
|
apiutil.Error(w, http.StatusBadRequest, "gateway_bootstrap_failed", "unable to bootstrap gateway")
|
|
return
|
|
}
|
|
|
|
apiutil.JSON(w, http.StatusOK, item)
|
|
}
|
|
|
|
func (h *Handler) AgentSyncBundle(w http.ResponseWriter, r *http.Request) {
|
|
if r.Header.Get("X-Gateway-Bootstrap-Token") != h.bootstrapToken {
|
|
apiutil.Error(w, http.StatusUnauthorized, "unauthorized", "invalid gateway bootstrap token")
|
|
return
|
|
}
|
|
|
|
bundle, err := h.service.BuildSyncBundle(r.Context(), chi.URLParam(r, "id"))
|
|
if err != nil {
|
|
apiutil.Error(w, http.StatusBadRequest, "gateway_sync_failed", "unable to build sync bundle")
|
|
return
|
|
}
|
|
|
|
apiutil.JSON(w, http.StatusOK, bundle)
|
|
}
|
|
|
|
func (h *Handler) Telemetry(w http.ResponseWriter, r *http.Request) {
|
|
if r.Header.Get("X-Gateway-Bootstrap-Token") != h.bootstrapToken {
|
|
apiutil.Error(w, http.StatusUnauthorized, "unauthorized", "invalid gateway bootstrap token")
|
|
return
|
|
}
|
|
|
|
var input TelemetrySnapshot
|
|
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
|
apiutil.Error(w, http.StatusBadRequest, "invalid_json", "invalid request body")
|
|
return
|
|
}
|
|
|
|
if err := h.service.StoreTelemetry(r.Context(), chi.URLParam(r, "id"), input); err != nil {
|
|
apiutil.Error(w, http.StatusBadRequest, "gateway_telemetry_failed", "unable to store gateway telemetry")
|
|
return
|
|
}
|
|
|
|
apiutil.JSON(w, http.StatusOK, map[string]any{"ok": true})
|
|
}
|