Skip to content

fix(editor): use direct scene operations and normalize loopback on sc… - #997

Open
ovurrsl wants to merge 3 commits into
pascalorg:mainfrom
ovurrsl:fix/windows-ssr-scene-routes
Open

ovurrsl wants to merge 3 commits into
pascalorg:mainfrom
ovurrsl:fix/windows-ssr-scene-routes

Conversation

@ovurrsl

@ovurrsl ovurrsl commented Oct 6, 2026 •

Copy link
Copy Markdown

fix(editor): read scenes directly on the scene pages, behind the scene API guard

Description

What does this PR do?

Fixes #876. On Windows, /scenes and /scene/<id> returned 500: the server-rendered pages loaded scenes by fetching their own /api/scenes endpoint over HTTP, and on Windows the pascal.localhost host in that URL does not resolve.

  • Both pages now call getSceneOperations().listScenes() / .loadStoredScene() directly. The API route uses that same singleton, so the HTTP round trip added nothing. There is no fallback and no localhost → 127.0.0.1 rewrite, so a storage error reaches the Next error page and isn't hidden.
  • Reading the store directly also bypassed the API's guard, so the pages now run the same check. The new apps/editor/lib/scene-page-guard.ts rebuilds a Request from next/headers and calls the existing guardSceneApiRequest. That applies the origin allow-list, auth (loopback, or PASCAL_SCENE_API_TOKEN as Authorization: Bearer / x-pascal-scene-token) and the per-IP rate limit, exactly as GET /api/scenes does. A denied request gets notFound() (HTTP 404). Server components cannot return a 401 without enabling Next's experimental authInterrupts, and a 404 doesn't reveal whether scenes exist.
  • SETUP.md: replaced the paragraph saying the Docker port must stay at 3000 (that limit only existed because the page fetched its own API). It now documents the guard.

What doesn't change: on localhost, *.localhost, 127.0.0.1 and ::1 with no token configured (the local dev / CLI setup), both pages work as before. A missing scene still renders the in-page "Scene not found" card.

Behaviour change: a page opened through a LAN IP or a public host with no PASCAL_SCENE_API_TOKEN configured now gets a 404. Before this PR it got an empty list or a 500, because its internal API fetch was refused (503) for the same reason.

How to test

  1. bun test apps/editor/lib (scene-page-guard.test.ts covers loopback hosts, a non-loopback host with no token (503), a required or wrong token (401), bearer and x-pascal-scene-token, an unlisted origin (403), the rate limit (429) and a missing host).
  2. bun dev, then open http://localhost:3002/scenes and a saved /scene/<id>. Both load. On Windows, also check http://pascal.localhost:3002/scenes.
  3. Open the same URLs through the machine's LAN IP. You get a 404. With PASCAL_SCENE_API_TOKEN=secret and a client that sends Authorization: Bearer secret (for example curl -H 'Authorization: Bearer secret' http://<ip>:3002/scenes), the page renders.

Screenshots / screen recording

None. Apart from the 404 for unauthorized hosts, there's no visual change.

Checklist

  • I've tested this locally with bun dev (not run here: this checkout lacks the private plugin packages the app imports; unit tests and type checks only)
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

Replies to bot review threads

(The GitHub API for pascalorg/editor wasn't reachable from this session, so these are keyed by topic. Paste each under the matching thread.)

  • pascal Bot / Cursor Bugbot — HTTP fallback or resolveBaseUrl / loopback rewrite (catch hides errors, duplicated helper): Removed: both pages call getSceneOperations() directly with no fallback, so storage errors surface, and both resolveBaseUrl copies are gone.
  • Any thread about the pages serving scenes without auth: Fixed: both pages now run guardSceneApiRequest (origin, loopback-or-token auth, rate limit) through lib/scene-page-guard.ts and return 404 when it refuses. Tests are in scene-page-guard.test.ts.
  • Any thread about NEXT_PUBLIC_APP_URL / the Docker port note in SETUP.md: The page no longer fetches its own API, so the port constraint is gone and SETUP.md now documents the guard instead.

…ene routes

Server components in /scenes and /scene/[id] self-fetched via HTTP over pascal.localhost, triggering getaddrinfo ENOTFOUND on Windows runtimes where *.localhost is not resolved by OS resolver, resulting in HTTP 500.

Query the scene operations singleton directly in server components, falling back to loopback fetch with .localhost normalized to 127.0.0.1.

Fixes pascalorg#876

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pascal

pascal Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

This PR fixes the HTTP 500 on /scenes and /scene/[id] described in #876. Both server components used to load data by fetching their own API routes over the request's host, which fails on Windows runtimes where *.localhost isn't resolved (getaddrinfo ENOTFOUND). They now call the scene operations singleton in lib/scene-store-server directly, the same one the /api/scenes routes use, and keep the HTTP fetch as a fallback with localhost and *.localhost hosts rewritten to 127.0.0.1 (preserving the port).

File Change What changed
apps/editor/app/scene/[id]/page.tsx modified fetchScene reads from getSceneOperations().loadStoredScene(id) first, falls back to the API fetch; resolveBaseUrl normalizes loopback hosts to 127.0.0.1
apps/editor/app/scenes/page.tsx modified fetchScenes reads from getSceneOperations().listScenes({ limit: 50 }) first, same fallback and loopback normalization

Start with fetchScene in apps/editor/app/scene/[id]/page.tsx: it shows both halves of the pattern, the direct-operations path and the bare catch that drops into the loopback fetch.

ovurrsl and others added 2 commits October 8, 2026 14:22
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FX8vGAP5g43AHR1jixUeXr
…allback

The /scenes and /scene/[id] pages read scenes straight from
getSceneOperations(), the same singleton the API route uses, so the HTTP
fallback with its loopback rewrite could only repeat the same failure while
the bare catch hid the real error. Remove it and let errors surface.

Reading the store directly also bypassed the scene API guard. Rebuild a
Request from the incoming headers and run guardSceneApiRequest (origin,
loopback-or-PASCAL_SCENE_API_TOKEN auth, rate limit) before loading;
denied requests get a 404. SETUP.md no longer claims the Docker port must
stay at 3000 because the page fetched its own API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FX8vGAP5g43AHR1jixUeXr

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows 1.0.0 runtime: /scenes and /scene/<id> routes return HTTP 500 (root canvas works)

1 participant