mirror of
https://github.com/luxfi/explorer.git
synced 2026-09-24 00:25:12 +00:00
Routing: use /v1/<svc> prefix, never /api/ (house convention)
The zip front router mounted service reverse-proxies under /api/<prefix>; renamed to /v1/<prefix> per convention (we never use a /api/ path prefix). Upstream service-internal /api/v1/... paths (behind the proxy) are unchanged.
This commit is contained in:
@@ -64,7 +64,7 @@ cgo, keeping their own HTTP servers on loopback.
|
||||
Go-native binary, no cgo — `ffi_off.go` stub). Scope services with
|
||||
`make single FFI_FEATURES=sig-provider,visualizer`.
|
||||
- **Front router (`front.go`):** zip owns the public listener. Routes, in order:
|
||||
`/api/<prefix>/*` → `httputil.ReverseProxy` → `127.0.0.1:<svc_port>`
|
||||
`/v1/<prefix>/*` → `httputil.ReverseProxy` → `127.0.0.1:<svc_port>`
|
||||
(prefix stripped); `/*` → the existing net/http explorer mux via
|
||||
`zip.AdaptNetHTTP`. Security headers + CORS are zip middleware.
|
||||
- **Config (`services:` in chains.yaml, `ServicesConfig`/`resolveServices`):**
|
||||
@@ -72,7 +72,7 @@ cgo, keeping their own HTTP servers on loopback.
|
||||
HTTP=base+2n/gRPC=+1), prefixes, and per-service settings JSON. The FFI
|
||||
launcher (`ffi_on.go startFFIServices`) and the proxy both read it.
|
||||
- **Status:** sig-provider WIRED + PROVEN end-to-end (one process owns :8090
|
||||
and :8050; `curl /api/sig/health → {"status":"SERVING"}`). visualizer +
|
||||
and :8050; `curl /v1/sig/health → {"status":"SERVING"}`). visualizer +
|
||||
smart-contract-verifier are stateless and ready to wire (recipe in
|
||||
`ffi/README.md`). stats + multichain-aggregator stay DISABLED:
|
||||
`blockscout-service-launcher` DB layer is Postgres-only (no SQLite without a
|
||||
|
||||
+4
-4
@@ -265,19 +265,19 @@ chains:
|
||||
|
||||
# In-process Blockscout-rs services (single-binary `-tags ffi` build only).
|
||||
# Each runs as a Tokio thread inside the explorer process; the zip front router
|
||||
# reverse-proxies /api/<prefix>/* -> http://127.0.0.1:<http_port>. In the
|
||||
# reverse-proxies /v1/<prefix>/* -> http://127.0.0.1:<http_port>. In the
|
||||
# default (non-FFI) build this whole block is inert. See ffi/README.md.
|
||||
services:
|
||||
enabled: false # master switch; true only in the `make single` binary
|
||||
list:
|
||||
# sig-provider — stateless 4byte/ABI signature lookup. WIRED + PROVEN.
|
||||
- name: sig-provider
|
||||
prefix: sig # -> /api/sig/* (e.g. /api/sig/health, /api/sig/api/v1/abi/function)
|
||||
prefix: sig # -> /v1/sig/* (e.g. /v1/sig/health, /v1/sig/api/v1/abi/function)
|
||||
http_port: 8050 # loopback; gRPC auto = http_port+1
|
||||
# --- ready to wire (stateless, no DB) — add the dep+feature+service! in ffi/ ---
|
||||
# - name: visualizer # -> /api/visualizer/*
|
||||
# - name: visualizer # -> /v1/visualizer/*
|
||||
# http_port: 8052
|
||||
# - name: smart-contract-verifier # -> /api/verifier/*
|
||||
# - name: smart-contract-verifier # -> /v1/verifier/*
|
||||
# http_port: 8054
|
||||
# --- Postgres-only (NOT sqlite-able via blockscout-service-launcher) ---
|
||||
# stats + multichain-aggregator need a real Postgres (stats also needs a
|
||||
|
||||
@@ -22,7 +22,7 @@ type Config struct {
|
||||
// ServicesConfig configures the in-process Blockscout-rs services that the
|
||||
// `-tags ffi` build runs as goroutine-driven Tokio threads inside this one
|
||||
// binary. The zip front router reverse-proxies each enabled service under
|
||||
// /api/<prefix>/* → http://127.0.0.1:<port>. In the default (non-FFI) build
|
||||
// /v1/<prefix>/* → http://127.0.0.1:<port>. In the default (non-FFI) build
|
||||
// nothing is launched and these proxies are not mounted (see services.go).
|
||||
type ServicesConfig struct {
|
||||
// Enabled is the master switch. When false (default) no in-process
|
||||
@@ -41,7 +41,7 @@ type ServiceConfig struct {
|
||||
// passed to it.
|
||||
Name string `yaml:"name"`
|
||||
// Prefix is the URL path segment the front router proxies under
|
||||
// (/api/<prefix>/*). Empty => derived from Name (sig-provider => "sig").
|
||||
// (/v1/<prefix>/*). Empty => derived from Name (sig-provider => "sig").
|
||||
Prefix string `yaml:"prefix"`
|
||||
// HTTPPort is the localhost port the service's own HTTP server binds.
|
||||
// Empty => a default is assigned per service (see servicePort).
|
||||
|
||||
+5
-5
@@ -114,13 +114,13 @@ own HTTP server bound to **loopback** (`127.0.0.1:<port>`, assigned from
|
||||
exposed. Routing, in registration order:
|
||||
|
||||
```
|
||||
/api/<prefix>/* → httputil.ReverseProxy → http://127.0.0.1:<svc_http_port> (mountServiceProxies, services.go)
|
||||
/v1/<prefix>/* → httputil.ReverseProxy → http://127.0.0.1:<svc_http_port> (mountServiceProxies, services.go)
|
||||
/* → the existing Go-native explorer *http.ServeMux (zip.AdaptNetHTTP, front.go)
|
||||
```
|
||||
|
||||
The proxy strips the `/api/<prefix>` mount prefix, so the upstream sees its own
|
||||
native path (e.g. `/api/sig/health` → sig-provider `/health`;
|
||||
`/api/sig/api/v1/abi/function` → `/api/v1/abi/function`). Enabled services +
|
||||
The proxy strips the `/v1/<prefix>` mount prefix, so the upstream sees its own
|
||||
native path (e.g. `/v1/sig/health` → sig-provider `/health`;
|
||||
`/v1/sig/api/v1/abi/function` → `/api/v1/abi/function`). Enabled services +
|
||||
ports are configured under `services:` in chains.yaml
|
||||
(`ServicesConfig` → `resolveServices` is the single source of truth both the
|
||||
proxy and this FFI launcher read). Default prefixes:
|
||||
@@ -128,7 +128,7 @@ proxy and this FFI launcher read). Default prefixes:
|
||||
`multichain-aggregator→multichain`, `visualizer→visualizer`.
|
||||
|
||||
Proven end-to-end (sig-provider, one process owning both :8090 and the service
|
||||
port): `curl /api/sig/health → {"status":"SERVING"}`, matching a direct hit on
|
||||
port): `curl /v1/sig/health → {"status":"SERVING"}`, matching a direct hit on
|
||||
the in-process service port.
|
||||
|
||||
## Database: SQLite status per service (verified against explorer-rs)
|
||||
|
||||
@@ -4,7 +4,7 @@ package main
|
||||
//
|
||||
// One zip.App owns the public listener. It composes two things, in order:
|
||||
//
|
||||
// 1. /api/<prefix>/* reverse-proxies to each in-process Blockscout-rs
|
||||
// 1. /v1/<prefix>/* reverse-proxies to each in-process Blockscout-rs
|
||||
// service (mountServiceProxies, services.go). Registered FIRST so Fiber's
|
||||
// in-order route matching sends these to the Rust services.
|
||||
//
|
||||
@@ -53,7 +53,7 @@ func buildFrontApp(base http.Handler, svcs []resolvedService) *zip.App {
|
||||
})
|
||||
|
||||
// (1) Per-service reverse proxies — registered before the catch-all so
|
||||
// /api/<prefix>/* wins over /*.
|
||||
// /v1/<prefix>/* wins over /*.
|
||||
mountServiceProxies(app, svcs)
|
||||
|
||||
// (2) The whole existing explorer mux as the catch-all. zip.AdaptNetHTTP
|
||||
|
||||
@@ -192,7 +192,7 @@ func main() {
|
||||
supervisor.MountRoutes(mux)
|
||||
frontend.Mount(mux)
|
||||
|
||||
// zip is the FRONT router (front.go): it reverse-proxies /api/<prefix>/*
|
||||
// zip is the FRONT router (front.go): it reverse-proxies /v1/<prefix>/*
|
||||
// to each in-process service and serves everything else from the explorer
|
||||
// mux built above. When no services are configured the front app is just
|
||||
// the mux behind zip's security middleware — identical behaviour, one
|
||||
|
||||
+8
-8
@@ -11,7 +11,7 @@ package main
|
||||
// 2. SETTINGS — build the JSON each service's upstream `Settings` deserializes
|
||||
// (server.http.addr / server.grpc.addr, and DB url where applicable).
|
||||
// 3. PROXY — mount a reverse-proxy on the zip front App at
|
||||
// /api/<prefix>/* → http://127.0.0.1:<http_port>.
|
||||
// /v1/<prefix>/* → http://127.0.0.1:<http_port>.
|
||||
//
|
||||
// The actual in-process LAUNCH (cgo → liblux_explorer_ffi.a) is the ONLY part
|
||||
// that differs by build tag and lives in ffi_on.go / ffi_off.go. That split is
|
||||
@@ -38,7 +38,7 @@ import (
|
||||
const servicePortBase = 8050
|
||||
|
||||
// defaultPrefix maps an FFI service name to its default URL prefix. The prefix
|
||||
// is the path segment the SPA / clients hit: /api/<prefix>/*. Kept short and
|
||||
// is the path segment the SPA / clients hit: /v1/<prefix>/*. Kept short and
|
||||
// stable so front-end calls don't churn when a service is renamed upstream.
|
||||
var defaultPrefix = map[string]string{
|
||||
"sig-provider": "sig",
|
||||
@@ -159,24 +159,24 @@ func buildSettingsJSON(s ServiceConfig, httpPort, grpcPort int) string {
|
||||
}
|
||||
|
||||
// mountServiceProxies registers a reverse-proxy on the zip front App for each
|
||||
// resolved in-process service: /api/<prefix>/* → http://127.0.0.1:<http_port>.
|
||||
// resolved in-process service: /v1/<prefix>/* → http://127.0.0.1:<http_port>.
|
||||
//
|
||||
// We strip the /api/<prefix> mount prefix before forwarding so the upstream
|
||||
// We strip the /v1/<prefix> mount prefix before forwarding so the upstream
|
||||
// service sees the path it expects (e.g. /api/v1/abi/... not
|
||||
// /api/sig/api/v1/abi/...). zip.AdaptNetHTTP wraps the stdlib
|
||||
// /v1/sig/api/v1/abi/...). zip.AdaptNetHTTP wraps the stdlib
|
||||
// httputil.ReverseProxy onto a zip route — the simplest thing that works, per
|
||||
// the brief: no bespoke fasthttp proxy, no escape hatch.
|
||||
//
|
||||
// Mounted via app.Mount which registers `All(prefix+"/*")`. These are
|
||||
// registered BEFORE the catch-all explorer mux (see buildFrontApp) so Fiber's
|
||||
// in-order matching routes /api/<prefix>/* to the service and everything else
|
||||
// in-order matching routes /v1/<prefix>/* to the service and everything else
|
||||
// to the explorer.
|
||||
func mountServiceProxies(app *zip.App, svcs []resolvedService) {
|
||||
for _, s := range svcs {
|
||||
target := &url.URL{Scheme: "http", Host: fmt.Sprintf("127.0.0.1:%d", s.HTTPPort)}
|
||||
mountPrefix := "/api/" + s.Prefix
|
||||
mountPrefix := "/v1/" + s.Prefix
|
||||
rp := httputil.NewSingleHostReverseProxy(target)
|
||||
// Strip the /api/<prefix> mount prefix so the upstream sees its own
|
||||
// Strip the /v1/<prefix> mount prefix so the upstream sees its own
|
||||
// native path. NewSingleHostReverseProxy's default Director only sets
|
||||
// the host; we extend it to rewrite the path.
|
||||
base := rp.Director
|
||||
|
||||
Reference in New Issue
Block a user