Skip to content

Commit 51c1bd9

Browse files
rlafurudclaude
andcommitted
feat(hybrid): route /launch to a local backend from the deployed site
- api.js: launch() uses VITE_LAUNCH_BASE (falls back to the main API base). Set VITE_LAUNCH_BASE=http://localhost:8000 on the cloud build so the deployed site generates on the cloud but launches the real game on the user's PC. - app.py: respond to Chrome's Private Network Access preflight so an HTTPS page may call the http://localhost backend. - App.jsx: friendlier launch error pointing to start_local.bat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 694cf0a commit 51c1bd9

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

backend/app.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@
5555
)
5656

5757

58+
# A deployed HTTPS page (e.g. the Vercel site) calling this local backend at
59+
# http://localhost:8000 triggers Chrome's Private Network Access check, which
60+
# needs this header on the preflight response. Added after CORS so it wraps the
61+
# CORS preflight reply. Harmless on the cloud backend (header just unused).
62+
@app.middleware("http")
63+
async def allow_private_network(request, call_next):
64+
response = await call_next(request)
65+
if request.headers.get("access-control-request-private-network"):
66+
response.headers["Access-Control-Allow-Private-Network"] = "true"
67+
return response
68+
69+
5870
class GenerateRequest(BaseModel):
5971
query: str
6072
seed: int | None = None

frontend/src/App.jsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ export default function App() {
4747
setLaunchMsg({ ok: false, text: `실행 실패: ${res.error || '알 수 없는 오류'}` })
4848
}
4949
} catch (e) {
50-
setLaunchMsg({ ok: false, text: `백엔드에 연결하지 못했어요 (${e.message}).` })
50+
// Most common cause on the deployed site: launch is routed to the user's
51+
// local backend, which isn't running.
52+
setLaunchMsg({
53+
ok: false,
54+
text: `로컬 백엔드에 연결하지 못했어요. 내 PC에서 start_local.bat을 실행해 켜두세요. (${e.message})`,
55+
})
5156
} finally {
5257
setLaunching(false)
5358
}

frontend/src/api.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
// double slash ("//generate") that the backend 404s on.
66
const BASE = (import.meta.env.VITE_API_BASE ?? '/api').replace(/\/+$/, '')
77

8+
// /launch opens the real game on the machine running the backend, so it must
9+
// hit a backend with BAR installed. Defaults to the same backend as everything
10+
// else (local single-process). On a cloud deploy set
11+
// VITE_LAUNCH_BASE=http://localhost:8000 to route launch to the user's own
12+
// local backend while generation stays on the cloud.
13+
const LAUNCH_BASE = (import.meta.env.VITE_LAUNCH_BASE ?? BASE).replace(/\/+$/, '')
14+
815
export async function getCatalog() {
916
const res = await fetch(`${BASE}/catalog`)
1017
if (!res.ok) throw new Error(`catalog failed: ${res.status}`)
@@ -23,7 +30,7 @@ export async function generate(query, seed) {
2330

2431
// Launch the generated config in the actual BAR client (via the v4 simulator).
2532
export async function launch(config, mode = 'gadget') {
26-
const res = await fetch(`${BASE}/launch`, {
33+
const res = await fetch(`${LAUNCH_BASE}/launch`, {
2734
method: 'POST',
2835
headers: { 'Content-Type': 'application/json' },
2936
body: JSON.stringify({ config, mode }),

0 commit comments

Comments
 (0)