Commit Graph
6 Commits
Author SHA1 Message Date
zeekay 3959357be3 feat(v1.3.0): drop embedded SPA — per-brand FE Deployment owns the UI
The Go binary is now a data-layer service only. The `/` catch-all that
served an embedded SPA shell is gone; v1.3.0 keeps just the endpoints
the FE depends on:

  - /v1/*       — indexer + blockscout-compat REST APIs
  - /health     — health probe
  - /envs.js    — runtime brand/chain config (window.ENV)
  - /icon.svg, /logo.svg — per-host brand SVGs (overlay-friendly)
  - /icon-{slug}.svg, /logo-{slug}.svg — extra named brand assets

Every other path returns 404 so the IngressRoute hands it off to the
per-brand Blockscout FE Deployment (ghcr.io/{brand-org}/explore-{env}).

Concrete deltas:
  - frontend.go: drop handleSPA, writeIndex, f.index; replace with
    handleNamedBrandAsset + brandAssetNames discovery
  - frontend_test.go: TestFrontendNoSPA + TestFrontendBrandAssetsServed
    replace the index-html overlay tests
  - Dockerfile: drop Next.js frontend build stage; runtime image is just
    the Go binary + brand SVGs (no embedded HTML)
  - static/index.html: removed (60-byte placeholder)

The per-brand FE Deployments + IngressRoute patches that complete this
split live in the cluster (lux-mainnet + lux-testnet namespaces). Source
of the FE images: luxfi/explore via Dockerfile.branded.
2026-06-10 13:21:01 -07:00
zeekay 366e4c57d2 fix(spa): brand merging + env-tag canonicalisation
- BRAND is now a merge of SERVER_BRAND (chains.yaml-supplied) and
  INFERRED_BRAND (host parser table). Server wins on name/coin/accent
  when it's non-default; inferred fills in iconUrl/logoUrl when the
  chain config doesn't carry per-brand SVG paths. Stops Zoo rendering
  with the default Lux favicon when chains.yaml omits icon_url.
- networkFromHost now canonicalises "test"→"testnet", "dev"→"devnet"
  so the dropdown label matches the server-side VITE_CURRENT_NETWORK.
2026-06-10 11:21:02 -07:00
zeekay c75790d446 fix(spa): client-side network derivation fallback for older images
The Go-side networksForHost was added in v1.2.20 but the deployed
image is still v1.2.18 (CI pool gap — lux-build AutoscalingRunnerSet
is missing from arc-system). The SPA now also parses location.host
client-side so the network switcher works when /envs.js doesn't ship
VITE_CURRENT_NETWORK / VITE_NETWORK_HOSTS.

Same shape rules as the Go side: explore.{brand}.{tld} (mainnet,
preserve tld), explorer.{brand}-test.network (testnet, .network is
canonical). Numeric and *.local hosts skip the dropdown.

This lets us ship the cross-network switcher via an EXPLORER_STATIC_DIR
overlay (ConfigMap-mounted SPA) without rebuilding the image.
2026-06-10 11:07:39 -07:00
zeekay 248e16dcb4 feat(frontend): cross-network switcher (mainnet ↔ testnet) in SPA
handleEnvs now returns VITE_CURRENT_NETWORK + VITE_NETWORK_HOSTS so
the SPA can render a Network dropdown next to the Chain picker. When
the user picks "testnet" from explore.zoo.network the SPA redirects
to explorer.zoo-test.network (and vice-versa).

Sibling host derivation lives entirely in Go:
- parseBrandEnv extracts (brand, env, dotted, ok) from any of our
  deployed host shapes — explore.{brand}.tld, explorer.{brand}-test.network,
  explore-{brand}.lux.network, explore-{brand}-test.lux.network.
- networksForHost composes the sibling map. Mainnet TLD is preserved
  when the user is on a brand-owned host (explore.hanzo.ai stays .ai);
  testnet is always {brand}-test.network because that's the canonical
  shape.
- isValidBrand rejects numeric stems so an IP host (10.0.0.1) doesn't
  fall through to "explore.10.0.0.1" / "explorer.10-test.network".

SPA renders the dropdown above the chain picker; hidden when the host
yields fewer than two siblings (local dev, admin tooling).

Tests cover all four canonical surfaces (lux+zoo × mainnet+testnet),
hanzo's TLD asymmetry (.ai mainnet → .network testnet), the
explore-{slug}.lux.network prefix shape, and the unparseable-host
guards (IPs, in-cluster service names, localhost).
2026-06-10 11:05:15 -07:00
zeekay 1e602d24e9 feat(frontend): runtime static-dir overlay + substantive SPA shell
Two related changes that get the explorer SPA rendering real chain
data without waiting for upstream luxfi/explore to ship a Next.js
static export.

1. Runtime SPA overlay (config.go, frontend.go)

   New cfg.StaticDir (also EXPLORER_STATIC_DIR env var). When set,
   the frontend prefers the on-disk copy of any static asset over the
   embedded FS — so a ConfigMap-mounted SPA shell replaces the
   embedded 60-byte placeholder without rebuilding the binary. The
   embedded FS remains the source of truth at build time and the
   fallback when no overlay is configured.

   handleSPA + serveBrandFile both check overlay > embedded. Tests
   cover overlay-present, overlay-missing-file fallback, no-overlay,
   and env-var-only configuration.

2. Substantive SPA shell (static/index.html)

   Replaced the 60-byte stub (<div id="root"></div>) with a vanilla
   JS SPA — ~7KB, zero dependencies, no React/Next.js. Reads
   window.ENV from /envs.js, fetches /v1/indexer/{slug}/{stats,blocks,
   transactions}, renders KPIs + Latest Blocks + Latest Transactions
   with per-chain switching and brand color/title overrides. Polls
   every 15s. Stores selected chain in URL hash + localStorage.

   This is the explorer's default UI until upstream luxfi/explore
   ships output: 'export' and the Dockerfile's optional pnpm-build
   overlay activates.

Dockerfile: drop the leftover merge-conflict markers around the SPA
build stage, keep the explanatory comment.

Tests: 4 new in frontend_test.go covering overlay precedence + env-var
fallback. All existing tests (registry, realtime) still pass.
2026-06-05 11:05:53 -07:00
zeekay 0a97dedfe9 unified explorer: single Go binary with embedded SPA + per-chain graph
Module renamed to github.com/luxfi/explorer (was -unified). Imports
luxfi/indexer/{daemon,evm,storage,explorer} and luxfi/graph/{engine,
indexer,storage} as libraries; embeds the luxfi/explore SPA via go:embed
all:static.

A new ChainSupervisor wraps the existing ChainRegistry: adding a chain
(via config / mDNS / admin API) spawns a per-chain indexer goroutine plus
one graph engine + indexer per enabled subgraph; removing cancels them
and frees the routes from a sync.Map dispatcher mounted on /v1/indexer/,
/v1/explorer/ (legacy alias), and /v1/graph/.

Config schema is fully per-chain: indexer poll interval, list of graph
subgraphs (each can pick a built-in schema like amm, securities, fhe…),
brand (name/coin/accent + on-disk icon/logo paths), token features, and
top-level networks list. /envs.js exposes the chain list and active
brand to the SPA at runtime, so a deploy can rebrand by remounting
chains.yaml + assets — no image rebuild.

Dockerfile clones luxfi/explore + luxfi/indexer + luxfi/graph as
siblings inside the build, populates ./static from the SPA build, and
produces one binary. CI checks out the three repos as siblings so the
local replace in go.mod works without tagged releases.
2026-05-05 21:03:52 -07:00