mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Drops "subnet" from the P-chain surface, which is not our vocabulary:
useSubnets -> useNets (useSubnets.ts deleted)
PChainSubnet -> PChainNet
GetSubnetsResponse -> GetNetsResponse
One "subnet" deliberately survives — `subnetID` in lib/api/pchain/wire.spec.ts.
That is the UPSTREAM P-chain wire field, read through a raw
Record<string,string> cast, so renaming it would break response parsing. The
rule is about our own language, not about lying to the wire.
Also adds deploy/k8s/explore-fe/ingress-mainnet.yaml. explore.lux.network is
served by the Next.js deployment explore-fe-lux (ghcr.io/luxfi/explore), not
the Go `explorer` binary, which only backs api-explore.lux.network. This
replaces drift where the plain Ingress pointed explore.lux.network at
explorer:80 — the Go binary's embedded, stale build.
Two pre-commit gates had to be satisfied, both legitimate:
- eslint max-len: three className lines (BridgePage 230 chars, ChainRow 188 x2)
exceeded the 160 limit; wrapped.
- cspell: `platformvm` flagged unknown. It is the P-chain VM's actual name, so
it is added to cspell.jsonc `words` rather than renamed in code. (Note for
the next person: .cspell-words.txt is listed under ignorePaths — it is a file
excluded from checking, NOT the dictionary. Adding a word there does nothing.)
Verified: tsc --noEmit reports ZERO errors in every file this commit touches and
zero stale-symbol errors, so the rename resolves completely. The project's 223
pre-existing errors are all in untouched files and unchanged by this commit.
58 lines
2.0 KiB
TypeScript
58 lines
2.0 KiB
TypeScript
// Request-scoped hostname store for stateless SSR.
|
|
//
|
|
// Next.js pages router bakes `NEXT_PUBLIC_APP_HOST` at BUILD time — the
|
|
// same image served from 6 different hostnames would SSR with the same
|
|
// brand every time. That kills horizontal scaling because every deploy
|
|
// must be rebuilt per hostname.
|
|
//
|
|
// This store captures `req.headers.host` at the start of each SSR render
|
|
// (see `_document.tsx`), so `getHostname()` on the server returns the
|
|
// actual requested host. The same docker image serves all chains on the
|
|
// Lux primary network (C, Zoo, Hanzo, SPC, Pars L1s) at distinct
|
|
// explore-*.lux.network hosts — one image, N hosts.
|
|
//
|
|
// Client side is unaffected: `window.location.hostname` continues to be
|
|
// the source of truth post-hydration.
|
|
|
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
|
|
const store = new AsyncLocalStorage<string>();
|
|
|
|
/**
|
|
* Run an async function with a captured request hostname. All calls to
|
|
* `getRequestHost()` inside the callback (or transitively) will receive
|
|
* this hostname.
|
|
*/
|
|
export function withRequestHost<T>(host: string, fn: () => Promise<T> | T): Promise<T> | T {
|
|
return store.run(host, fn);
|
|
}
|
|
|
|
/**
|
|
* Returns the hostname of the currently-rendering SSR request, or an
|
|
* empty string when called outside a scoped run (e.g. during build,
|
|
* static pages, or edge functions where no request is bound).
|
|
*/
|
|
export function getRequestHost(): string {
|
|
return store.getStore() ?? '';
|
|
}
|
|
|
|
/**
|
|
* Parse a raw Host header ("explore.zoo.network:3000") into just the
|
|
* hostname part. Handles IPv4, IPv6 (bracketed), and missing port.
|
|
*/
|
|
export function parseHostHeader(hostHeader: string | undefined): string {
|
|
if (!hostHeader) return '';
|
|
const h = hostHeader.trim();
|
|
// IPv6 in brackets: [::1]:3000
|
|
if (h.startsWith('[')) {
|
|
const end = h.indexOf(']');
|
|
return end > 0 ? h.slice(1, end) : h;
|
|
}
|
|
// Strip :port
|
|
const colon = h.lastIndexOf(':');
|
|
if (colon > 0 && /^\d+$/.test(h.slice(colon + 1))) {
|
|
return h.slice(0, colon);
|
|
}
|
|
return h;
|
|
}
|