feat: add gateway bootstrap endpoint with token-based authentication

Add Bootstrap and AgentSyncBundle handlers to gateway package with X-Gateway-Bootstrap-Token header authentication. Implement UpsertByName repository method for idempotent gateway registration. Update gateway entrypoint script to auto-generate keys and bootstrap gateway on first run, persisting gateway ID to disk. Add GATEWAY_BOOTSTRAP_TOKEN config and update environment variables for gateway name, bootstrap URL, and sync URL.
This commit is contained in:
2026-03-17 18:53:26 +01:00
parent a197fb5bb6
commit 16fc6cb1b6
9 changed files with 138 additions and 9 deletions

View File

@@ -10,11 +10,12 @@ import (
)
type Handler struct {
service *Service
service *Service
bootstrapToken string
}
func NewHandler(service *Service) *Handler {
return &Handler{service: service}
func NewHandler(service *Service, bootstrapToken string) *Handler {
return &Handler{service: service, bootstrapToken: bootstrapToken}
}
func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
@@ -52,3 +53,39 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
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)
}