Stellar Prices API — API keys
+ return probe; +} - {probe.state === 'loading' &&Checking whether the portal is open…
} +/** + * What the three routes share: the portal's flag, the session, and the two + * questions every route answers differently. + */ +type Gate = { + probe: Probe; + /** The portal is confirmed open. Not "not confirmed shut" — see `canOfferKey`. */ + open: boolean; + session: SessionState; + /** The session lookup has an answer, whatever it is. */ + settled: boolean; + authenticated: boolean; + onSignOut: () => void; + /** + * Re-ask `/auth/me`. The sign-in popup needs it: the session cookie appears + * without this window ever navigating, so nothing else would tell the app + * that the visitor is now signed in. + */ + reloadSession: () => void; +}; - {/* The closed state is the one that ships. Task 0194 flips the flag, - after task 0189's eligibility gate passes — so for now this is what - every visitor sees, and it must say so plainly and offer nothing to - click. No sign-in control: the backend answers those routes with an - empty 404 while the flag is off, so a button here would be a button - that cannot work. */} - {probe.state === 'ok' && !probe.config.enabled && ( -- This is where you will sign in and issue an API key. It is not yet - available. -
- )} +/** + * `/` — the landing page, and the junction the OAuth callback lands on. + * + * `portal/auth/mod.rs` redirects to `/api-tokens/` in **every** outcome and + * says why: "when the portal grows a second page, the page it lands on decides + * where to go next; this handler still will not." This is that page, and these + * are the two decisions it makes. + * + * Both forwards carry `location.search` verbatim. The callback's `?issue=…` + * and `?signin=…` are one-shot landing states owned by tasks 0189 and 0186, + * read and stripped by whichever view renders them — dropping the query here + * would silently delete a refusal the visitor is owed an explanation for. + * + * The redirects are `replace`, so Back from the dashboard goes to wherever the + * visitor was before signing in rather than to a `/` that bounces them + * straight forward again. + */ +function RootRoute({ gate }: { gate: Gate }) { + const location = useLocation(); + const canOfferKey = gate.open; - {/* Reachable only once PORTAL_ENABLED is true. Task 0186 put sign-in - here; issuing a key is task 0187's. */} - {probe.state === 'ok' && probe.config.enabled && ( -
- Could not reach the portal backend: {probe.reason}
-
- - {/* The same-origin proof, and the reason it is this route rather than the - `/api-tokens/api/health` named in task 0185's criteria: that route does - not exist. The portal backend maps `/config` and, from task 0186, - `/auth/*`; task 0183's gate answers an empty 404 on every other path - under the prefix — so a `/health` probe would render a failure whether - or not anyone implemented it. `/config` answers 200 in BOTH flag - states, which is what makes it the honest probe. */} - {/* Three branches, not a two-way ternary on `=== 'ok'`. While the probe - is in flight the answer is not yet known, and a ternary claimed - "unsuccessfully" on first paint — so the page said it had failed to - reach the backend at the same time as saying it was still asking. This - paragraph is the acceptance criterion's evidence of a live call; it has - to be silent about the outcome until there is one. */} - {probe.state === 'loading' ? ( -
- Calling /api-tokens/api/config — same-origin, no API key,
- no CORS.
-
- Reached /api-tokens/api/config{' '}
- {probe.state === 'ok' ? 'successfully' : 'unsuccessfully'} —
- same-origin, no API key, no CORS.
-