package api import ( "errors" "net/http" "github.com/go-chi/chi/v5" "github.com/hanzoai/bootnode/api/handlers" "github.com/hanzoai/bootnode/api/middleware" bootnodezap "github.com/hanzoai/bootnode/zap/gen" ) // Mount registers every Bootnode HTTP route + ZAP service onto r, // using deps for cross-subsystem calls. This is the HIP-0106 // subsystem contract: superbase calls Mount on the unified binary's // root mux, cmd/bootnode calls it on a fresh chi.Mux. // // Routes are grouped by FastAPI parity: // // GET /health liveness // GET /ready readiness (deps reachable) // // POST /v1/auth/register local-dev auth (TODO: IAM-only in prod) // POST /v1/auth/login // GET /v1/auth/me // POST /v1/auth/oauth/callback IAM OAuth code exchange // // GET /v1/chains list supported chains // GET /v1/chains/{chain} chain details // // POST /v1/rpc/{chain} JSON-RPC proxy // POST /v1/rpc/{chain}/{network} // // GET /v1/tokens/{chain}/balances/{address} // GET /v1/tokens/{chain}/metadata/{contract} // POST /v1/tokens/{chain} (batched) // POST /v1/tokens/{chain}/{network} // // GET /v1/nfts/{chain}/owned/{address} // GET /v1/nfts/{chain}/collection/{contract} // GET /v1/nfts/{chain}/metadata/{contract}/{tokenId} // POST /v1/nfts/{chain}/refresh/{contract}/{tokenId} // // GET /v1/transfers/{chain}/address/{address} // GET /v1/transfers/{chain}/tx/{txHash} // POST /v1/transfers/{chain}/send // // POST /v1/wallets/create // GET /v1/wallets // GET /v1/wallets/{address} // POST /v1/wallets/{address}/estimate // GET /v1/wallets/{address}/nonce // // POST /v1/bundler/{chain} ERC-4337 RPC // POST /v1/bundler/{chain}/{network} // // GET /v1/gas/{chain}/prices // POST /v1/gas/sponsor // POST /v1/gas/estimate // GET /v1/gas/policies // POST /v1/gas/policies // GET /v1/gas/policies/{id} // DELETE /v1/gas/policies/{id} // // POST /v1/webhooks // GET /v1/webhooks // GET /v1/webhooks/{id} // PATCH /v1/webhooks/{id} // DELETE /v1/webhooks/{id} // GET /v1/webhooks/{id}/deliveries // POST /v1/webhooks/{id}/test // // POST /v1/auth-keys/projects // GET /v1/auth-keys/projects/{id} // POST /v1/auth-keys/keys // GET /v1/auth-keys/keys // DELETE /v1/auth-keys/keys/{id} // // (fleets, nodes, infra, team, networks, o11y, chat, zap) — same // parity, see handlers/*.go for the per-package router. func Mount(r chi.Router, deps Deps) error { if deps.Logger == nil { return errors.New("bootnode: deps.Logger is required") } // In-process ZAP service. zapc's generated dispatcher would // register this with the local dispatcher when superbase wires it; // the hand-rolled stub just holds the Service implementation. impl := handlers.NewService(handlers.ServiceDeps{ Logger: deps.Logger, IAM: deps.IAM, KMS: deps.KMS, Base: deps.Base, O11y: deps.O11y, Commerce: deps.Commerce, }) _ = bootnodezap.NewServer(impl) // hand to superbase ZAP dispatcher hdeps := handlers.Deps{ Logger: deps.Logger, Service: impl, IAM: deps.IAM, KMS: deps.KMS, Base: deps.Base, O11y: deps.O11y, Commerce: deps.Commerce, } // Health endpoints are mounted at the root — superbase aggregates // them but Bootnode-as-standalone needs them addressable too. r.Get("/health", handlers.Health(hdeps)) r.Get("/ready", handlers.Ready(hdeps)) r.Route("/v1", func(r chi.Router) { r.Use(middleware.Telemetry(deps.O11y)) r.Use(middleware.AuthOptional(deps.IAM)) handlers.MountAuth(r, hdeps) handlers.MountAuthKeys(r, hdeps) handlers.MountChains(r, hdeps) handlers.MountRPC(r, hdeps) handlers.MountTokens(r, hdeps) handlers.MountNFTs(r, hdeps) handlers.MountTransfers(r, hdeps) handlers.MountWallets(r, hdeps) handlers.MountBundler(r, hdeps) handlers.MountGas(r, hdeps) handlers.MountWebhooks(r, hdeps) handlers.MountFleets(r, hdeps) handlers.MountNodes(r, hdeps) handlers.MountInfra(r, hdeps) handlers.MountTeam(r, hdeps) handlers.MountNetworks(r, hdeps) handlers.MountObservability(r, hdeps) handlers.MountChat(r, hdeps) handlers.MountBilling(r, hdeps) handlers.MountZAP(r, hdeps) }) // 404 for unknown paths inside our prefix range, leaving the rest // to superbase's other subsystems. r.NotFound(func(w http.ResponseWriter, req *http.Request) { http.Error(w, "not found", http.StatusNotFound) }) return nil }