Serve surfaces/shell's built Dashboard from apps/api on the same origin as /api (ADR 0044), exactly as the display host is served: a closed set of files read once at startup, never a static directory, never a path built from request input. - The shell exports @ekklesia/shell/dashboard-assets (the exact built index.html and script URLs), emits the script at assets/ekklesia-dashboard/app.js, and index.html names that absolute path so dev and production serve the same URLs. - GET / and GET /assets/ekklesia-dashboard/app.js answer from a frozen snapshot: no-cache with a strong ETag and 304 on match. Source maps are never served; every other path is the ordinary sanitized 404. - The page sets nosniff, X-Frame-Options: DENY, Content-Security-Policy: frame-ancestors 'none' (that directive only), and Referrer-Policy: no-referrer, on 200 and 304 alike. - A missing or empty shell build fails startup with a fixed DashboardHostAssetError before any module code is imported. - The If-None-Match comparison moves to conditional-request.ts, shared with the display host routes. - The emailed-link token stripping moves from browser/main.tsx into consumeAuthLanding (location and history injected) so it is tested; behavior is unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
59 lines
2.6 KiB
TypeScript
59 lines
2.6 KiB
TypeScript
import { installEkklesiaDesignTokens } from "@ekklesia/design-tokens";
|
|
import { EkklesiaUiStyles } from "@ekklesia/ui";
|
|
import { createRoot } from "react-dom/client";
|
|
import { consumeAuthLanding } from "../auth-protocol.js";
|
|
import type { EkklesiaHostConfig } from "../host-protocol.js";
|
|
import { HostApp } from "./HostApp.js";
|
|
|
|
/**
|
|
* Replaced at build time by `scripts/build-browser.mjs` with a JSON string literal: the API origin
|
|
* when `EKKLESIA_API_BASE_URL` was set for the build, and `""` otherwise. Declared rather than read
|
|
* from `process.env`, because a browser has no `process` and the substitution must be visible here
|
|
* instead of hidden in bundler configuration.
|
|
*/
|
|
declare const __EKKLESIA_API_BASE_URL__: string;
|
|
|
|
/**
|
|
* The one place the shell touches a browser global.
|
|
*
|
|
* An empty build-time value means the API is served from the same origin as this page, which is the
|
|
* expected production shape; a non-empty one is the split-origin development shape the platform's
|
|
* trusted-origin configuration already supports (`EKKLESIA_IDENTITY_TRUSTED_ORIGINS` must list this
|
|
* page's origin for the session cookie to travel). Everything downstream receives the resolved
|
|
* origin as a plain argument, so no other file in this package reads `window`.
|
|
*/
|
|
const config: EkklesiaHostConfig = {
|
|
apiBaseUrl: __EKKLESIA_API_BASE_URL__ || window.location.origin,
|
|
};
|
|
|
|
/** Read once, before anything renders, and stripped from the address bar in the same step. See
|
|
* `consumeAuthLanding` for why the token must not stay in the URL. */
|
|
const authLanding = consumeAuthLanding(window.location, window.history);
|
|
|
|
const container = document.getElementById("ekklesia-host");
|
|
if (!container) {
|
|
throw new Error("The Ekklesia shell requires a #ekklesia-host element.");
|
|
}
|
|
|
|
/**
|
|
* The host root installs the platform's default theme once, at the document (ADR 0039).
|
|
*
|
|
* This is what makes the tokens available to *everything* on the page, including each module UI
|
|
* artifact mounted inside its own isolated root: CSS custom properties inherit through a shadow
|
|
* boundary, so a module reads `var(--ekklesia-…)` without the host reaching into the artifact or the
|
|
* artifact importing the host. Only the values live here — the platform ships no theme editor, and
|
|
* an installation that wants different values overrides these declarations at its own root.
|
|
*/
|
|
installEkklesiaDesignTokens(document);
|
|
|
|
createRoot(container).render(
|
|
<>
|
|
<EkklesiaUiStyles />
|
|
{authLanding ? (
|
|
<HostApp authLanding={authLanding} config={config} />
|
|
) : (
|
|
<HostApp config={config} />
|
|
)}
|
|
</>,
|
|
);
|