mirror of
https://github.com/luxfi/explorer.git
synced 2026-09-24 00:25:12 +00:00
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.
106 lines
4.5 KiB
Docker
106 lines
4.5 KiB
Docker
# Unified Lux explorer image. Single Go binary, embedded SPA, indexer +
|
|
# graph as libraries. Deploy with a chains.yaml (or env vars) and persist
|
|
# /data; brand swap by remounting chains.yaml + assets, no rebuild needed.
|
|
|
|
ARG GO_VERSION=1.26
|
|
ARG NODE_VERSION=22
|
|
ARG EXPLORE_REPO=https://github.com/luxfi/explore.git
|
|
ARG EXPLORE_REF=main
|
|
ARG INDEXER_REPO=https://github.com/luxfi/indexer.git
|
|
ARG INDEXER_REF=main
|
|
ARG GRAPH_REPO=https://github.com/luxfi/graph.git
|
|
ARG GRAPH_REF=main
|
|
|
|
# ---- Stage 1: build the SPA ----
|
|
#
|
|
# luxfi/explore is a Next.js `output: 'standalone'` SSR app, not a static
|
|
# export — so `pnpm build` does NOT produce /app/out/. The unified
|
|
# explorer needs static assets to embed via go:embed; until upstream
|
|
# explore ships `output: 'export'`, we ship a substantive runtime SPA
|
|
# shell from explorer/static/. The shell wires /envs.js (window.ENV)
|
|
# to render the chain list client-side. When upstream explore lands
|
|
# static-export support, this stage gains `pnpm build && cp .next/static
|
|
# /app/out/_next/static` and the shell can hydrate.
|
|
#
|
|
# Build-arg defaults shape the optional Next.js static export attempt
|
|
# (only used if explore.git lands `output: 'export'` upstream). Real
|
|
# branding comes from the runtime chains.yaml + brand assets mount on
|
|
# the explorer Deployment — no rebuild needed to swap.
|
|
FROM node:${NODE_VERSION}-alpine AS frontend
|
|
ARG EXPLORE_REPO
|
|
ARG EXPLORE_REF
|
|
ARG NETWORK_ID=8675312
|
|
ARG NETWORK_NAME="regulated EVM L1"
|
|
ARG NETWORK_CURRENCY_SYMBOL=
|
|
RUN apk add --no-cache git
|
|
WORKDIR /app
|
|
# Best-effort: clone explore and try to produce a static export. If
|
|
# anything fails (missing pnpm-lockfile entry, missing env vars, SSR-only
|
|
# Next config), don't fail the image — explorer/static/ still ships.
|
|
RUN git clone --depth=1 --branch=${EXPLORE_REF} ${EXPLORE_REPO} . || true
|
|
ENV NEXT_PUBLIC_API_BASE_PATH=/v1/explorer
|
|
ENV NEXT_PUBLIC_NETWORK_ID=${NETWORK_ID}
|
|
ENV NEXT_PUBLIC_NETWORK_NAME=${NETWORK_NAME}
|
|
ENV NEXT_PUBLIC_NETWORK_CURRENCY_NAME=${NETWORK_CURRENCY_SYMBOL}
|
|
ENV NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=${NETWORK_CURRENCY_SYMBOL}
|
|
ENV NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
|
|
ENV NODE_OPTIONS=--max-old-space-size=8192
|
|
RUN if [ -f package.json ]; then \
|
|
corepack enable && pnpm install --frozen-lockfile --ignore-scripts && pnpm build || true; \
|
|
fi
|
|
# /app/out is created only if static export succeeded. The builder stage
|
|
# below COPYs it over explorer/static/ — but only if non-empty. The
|
|
# fallback path is the local explorer/static/index.html (substantive
|
|
# 372-byte SPA shell with id="root" + /envs.js bootstrap).
|
|
RUN mkdir -p /app/out
|
|
|
|
# ---- Stage 2: clone indexer + graph siblings, build the unified binary ----
|
|
FROM golang:${GO_VERSION}-alpine AS builder
|
|
ARG INDEXER_REPO
|
|
ARG INDEXER_REF
|
|
ARG GRAPH_REPO
|
|
ARG GRAPH_REF
|
|
ARG VERSION=dev
|
|
RUN apk add --no-cache gcc musl-dev sqlite-dev git
|
|
|
|
WORKDIR /src
|
|
RUN git clone --depth=1 --branch=${INDEXER_REF} ${INDEXER_REPO} indexer && \
|
|
git clone --depth=1 --branch=${GRAPH_REF} ${GRAPH_REPO} graph
|
|
|
|
WORKDIR /src/explorer
|
|
COPY . .
|
|
# explorer/static/ already contains the substantive SPA shell. Only
|
|
# overlay frontend /app/out/ if it has the static-exported assets (an
|
|
# index.html that's NOT the 60-byte stub). When upstream explore ships
|
|
# static-export, the cp succeeds and overlays the real assets.
|
|
RUN --mount=type=bind,from=frontend,source=/app/out,target=/tmp/frontend-out \
|
|
sh -eu -c '\
|
|
if [ -s /tmp/frontend-out/index.html ] && [ "$(wc -c </tmp/frontend-out/index.html)" -gt 100 ]; then \
|
|
echo "[frontend] overlaying static export onto explorer/static/"; \
|
|
cp -R /tmp/frontend-out/. /src/explorer/static/; \
|
|
else \
|
|
echo "[frontend] static export not produced, keeping committed explorer/static/ SPA shell"; \
|
|
fi'
|
|
|
|
# proxy.golang.org caches inconsistently for hanzoai/[email protected]
|
|
# (different POPs serve different zip hashes). -mod=mod populates go.sum
|
|
# from whatever the proxy serves at build time and GOSUMDB=off skips
|
|
# sum.golang.org cross-checks.
|
|
RUN rm -f go.sum && CGO_ENABLED=1 CGO_CFLAGS="-D_LARGEFILE64_SOURCE" GOSUMDB=off \
|
|
go build -trimpath -mod=mod \
|
|
-ldflags="-s -w -X main.version=${VERSION}" \
|
|
-o /out/explorer .
|
|
|
|
# ---- Stage 3: runtime ----
|
|
FROM alpine:3.21
|
|
RUN apk add --no-cache ca-certificates sqlite-libs wget
|
|
COPY --from=builder /out/explorer /usr/local/bin/explorer
|
|
RUN adduser -D -u 65532 explorer
|
|
USER explorer
|
|
VOLUME /data
|
|
ENV DATA_DIR=/data HTTP_ADDR=:8090
|
|
EXPOSE 8090
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
|
|
CMD wget -qO- http://localhost:8090/health || exit 1
|
|
ENTRYPOINT ["explorer"]
|