fa934ccdc6
The deployed application could never sign anybody in. `loadRuntimeConfig` skipped fetching `/festipod-config.json` under `NODE_ENV=production`, reasoning that a production build has the value inlined by `build.ts`'s `define`. This project's production does not build: the container copies the sources and runs `bun run start` (= `NODE_ENV=production bun src/index.ts`), serving from `src/` exactly as dev does. Nothing serves `dist/` at all. So the one step that could supply a wallet was skipped, `ensureIdentity()` threw for want of one, and the endpoint sat there — served, and never asked. The condition tested the wrong thing. "Was the value inlined?" is a question the global itself answers; "am I in production?" only ever stood in for it, and the stand-in was false on the very path that matters. Verified on the served bundle rather than on the endpoint: under `NODE_ENV=production` it now carries the fetch, where it carried none. The same combination is what `hooks.ts` spawns for the @e2e app server, so that layer could not sign in either.
75 lines
3.2 KiB
TypeScript
75 lines
3.2 KiB
TypeScript
/**
|
|
* This file is the entry point for the React app, it sets up the root
|
|
* element and renders the App component to the DOM.
|
|
*
|
|
* It is included in `src/index.html`.
|
|
*
|
|
* Before loading the app tree it pulls the RUNTIME shared-wallet config, sets
|
|
* the global, then dynamically imports `App` so `sharedWallet.ts` reads the
|
|
* value on evaluation. A `build.ts` bundle already carries the value inlined by
|
|
* `define`, and then there is nothing to fetch.
|
|
*
|
|
* WHICH ONE APPLIES IS NOT `NODE_ENV`. This used to skip the fetch under
|
|
* `NODE_ENV=production`, on the reasoning "production means built". This
|
|
* project's production does NOT build: the container copies the sources and
|
|
* runs `bun run start` (= `NODE_ENV=production bun src/index.ts`), serving from
|
|
* src/ exactly as dev does. Nothing ever serves `dist/`. So the deployed app
|
|
* skipped the only step that could give it a wallet, `ensureIdentity()` threw
|
|
* for want of one, and it could never sign anybody in — while `/festipod-config.json`
|
|
* sat there, served and unasked.
|
|
*
|
|
* The question is therefore "was the value inlined?", never "am I in
|
|
* production?" — ask the global itself.
|
|
*/
|
|
|
|
import { StrictMode } from "react";
|
|
import { createRoot } from "react-dom/client";
|
|
|
|
/** Fetch the runtime shared-wallet config and set the global, unless it is already there. */
|
|
async function loadRuntimeConfig(): Promise<void> {
|
|
// Already inlined by `build.ts`'s `define` → nothing to fetch. Bracket access,
|
|
// so that same `define` (which rewrites the dotted global) leaves this read alone.
|
|
if ((globalThis as Record<string, unknown>)["__FESTIPOD_SHARED_WALLET_PASSWORD__"] != null) return;
|
|
try {
|
|
const res = await fetch("/festipod-config.json");
|
|
if (!res.ok) return;
|
|
const cfg = (await res.json()) as { sharedWalletPassword?: string; autoSeed?: string };
|
|
// Bracket access so build.ts's `define` (which matches the dotted global)
|
|
// never rewrites this assignment. Only set when the env actually carries one.
|
|
const g = globalThis as Record<string, unknown>;
|
|
if (cfg.sharedWalletPassword && g["__FESTIPOD_SHARED_WALLET_PASSWORD__"] == null) {
|
|
g["__FESTIPOD_SHARED_WALLET_PASSWORD__"] = cfg.sharedWalletPassword;
|
|
}
|
|
// Auto-seed gate (see src/shared/utils/autoSeed.ts): only set the global when
|
|
// the env var carries a truthy value; absent → stays undefined → seed OFF.
|
|
if (cfg.autoSeed && g["__FESTIPOD_AUTO_SEED__"] == null) {
|
|
g["__FESTIPOD_AUTO_SEED__"] = cfg.autoSeed;
|
|
}
|
|
} catch {
|
|
// No runtime config endpoint (static build) → rely on the compile-time define.
|
|
}
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
await loadRuntimeConfig();
|
|
// Dynamic import AFTER the global is set, so sharedWallet.ts reads it on eval.
|
|
const { App } = await import("./App");
|
|
const elem = document.getElementById("root")!;
|
|
const app = (
|
|
<StrictMode>
|
|
<App />
|
|
</StrictMode>
|
|
);
|
|
|
|
if (import.meta.hot) {
|
|
// With hot module reloading, `import.meta.hot.data` is persisted.
|
|
const root = (import.meta.hot.data.root ??= createRoot(elem));
|
|
root.render(app);
|
|
} else {
|
|
// The hot module reloading API is not available in production.
|
|
createRoot(elem).render(app);
|
|
}
|
|
}
|
|
|
|
void main();
|