Add ServiceDNSRecord type and gateway API endpoint to expose active service domain-to-IP mappings. Implement ListServiceDNSRecords repository method querying services table with proxy_ip resolution using effectiveAccessProxyIP helper. Add vpn-dns microservice built on CoreDNS with periodic sync from backend API. Generate Corefile with configurable upstream DNS servers and hosts plugin for service overrides.
127 lines
3.7 KiB
Go
127 lines
3.7 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) AgentServiceDNS(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
|
|
}
|
|
|
|
items, err := h.service.ListServiceDNSRecords(r.Context())
|
|
if err != nil {
|
|
apiutil.Error(w, http.StatusInternalServerError, "service_dns_failed", "unable to build service dns records")
|
|
return
|
|
}
|
|
|
|
apiutil.JSON(w, http.StatusOK, map[string]any{"records": items})
|
|
}
|
|
|
|
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})
|
|
}
|