0G Storage 上傳改走同源代理,繞開 storage node 的 CORS - #11
Merged
Merged
Conversation
這兩點不是「線上顯示舊人物」的原因 —— 我原本以為是,實測後發現舊版程式在 同樣的 SPA fallback 條件下也能正常顯示三張照片,那個問題是部署內容裡沒有圖。 但這兩點本身仍是實際存在的脆弱處,順手修掉: 1) 探測改成四個候選同時載,不再一個一個等。 線上有 SPA fallback(找不到檔案回 index.html 200),不存在的副檔名不會快速 404,而是回一整份 HTML 才失敗。依序試的話總時間是四個相加;平行只花最慢的 那一個。仍依候選順序挑第一個成功的,結果是確定的。 2) 探測是非同步的,但畫面同步檢查 heroSheetReady()。網路慢的時候簡報畫面 可能先用 SVG 畫好,探測晚一點才回來說圖其實在,那時畫面不會自己更新, 玩家就永遠看到備援版本。把英雄卡的繪製抽成 renderHeroCards(), 探測完成後如果簡報畫面還開著就就地重畫。 在模擬 Cloudflare SPA fallback 的伺服器上驗過(缺檔回 200 text/html): 按下「跳過劇情」後 0.4 秒就是三張形象照,採用 /images/heroes.jpeg,無 JS 錯誤。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015NBxaWtzqEX3i8NvoZEeq3
診斷收斂後確認:瀏覽器打得到 indexer,卡在後面。SDK 上傳一個檔案要跟兩種
主機講話 —— 先問 indexer「該傳給哪些 storage node」,再拿那份清單裡的 url
直接打每一台節點的 zgs_uploadSegment。第一步通,第二步不通:那些節點是各自
獨立的主機,沒有為瀏覽器開 CORS,axios 只回一句沒有內容的 "Network Error"。
新增 functions/api/og/zg/[[path]].js 把兩步都繞過自家 Worker:
前端 new Indexer('/api/og/zg/indexer')
├─ 轉發到真的 indexer,並把回應裡每個節點的 url 改寫成
│ /api/og/zg/node/<編碼後的原始網址>
└─ 節點請求再轉發到那台真的 storage node
這樣瀏覽器全程只跟自己的網域講話,沒有跨來源請求。Flow 合約那步不經過代理,
它走 MetaMask 簽名,本來就沒有 CORS 問題;EVM RPC 也維持直連(實測通得過)。
節點清單的形狀在 SDK 版本之間變過(有時陣列、有時 { trusted: [...] } 分片
物件),所以改寫不去假設結構,直接走訪整棵 JSON,看到叫 url 且是 http(s)
開頭的字串就換掉。非 0g.ai 的主機原樣留著讓它自己失敗,不靜悄悄吞掉。
安全性:只轉發到 0g.ai(與其子網域)的 https 主機,否則就是一個任何人都能
借用的開放代理。轉發時只帶 content-type,不把 cookie、authorization 之類的
標頭交給第三方主機。
驗證:
- 用假的 indexer/節點驅動 Function 本體:節點網址正確改寫、編碼後的網址能
還原成原始目標(含 port 與 path)、method 與 body 原樣帶過去。
- 白名單四種繞過嘗試都被擋(非 0g.ai、http 明文、not0g.ai 這種後綴冒充、
0g.ai.evil.com 這種子網域冒充),合法的 0g.ai 放行。
- wrangler pages dev 確認巢狀 [[path]] catch-all 路由解析正確,未知路徑回
404,且 /api/og/storage 與 /api/og/status 不受影響。
實際上傳仍需在瀏覽器實測 —— 這個開發環境連不到 0g.ai。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NBxaWtzqEX3i8NvoZEeq3
There was a problem hiding this comment.
🟡 Changes recommended
代理轉發目前以文字方式讀取/轉送 request body 可能破壞二進位上傳 payload,且部分 API 行為變更(忽略 indexer 參數)需要同步調整以避免誤用與誤診斷。
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
這個 PR 針對 0G Storage 在瀏覽器端上傳時會被 storage node 的 CORS 擋下的問題,新增同源代理路由,讓前端上傳流程全程只打自己網域,並順手改善簡報畫面英雄形象照的載入/更新時序。
Changes:
- 新增
/api/og/zg/*同源代理(indexer 轉發 + 回應節點 URL 改寫 + node 轉發),用來繞開 storage node 的 CORS。 - 前端上傳流程改用同源 proxy indexer,避免 SDK 直連 storage node。
- 英雄形象照探測改為並行載入,並在探測完成後可就地更新簡報畫面卡片。
File summaries
| File | Description |
|---|---|
| public/js/storage.js | 將 0G Storage indexer 預設改成同源代理,並調整上傳時的 indexer 初始化方式。 |
| public/js/main.js | 抽出並重用英雄卡片渲染,探測完成後可在簡報畫面已顯示時就地更新。 |
| public/js/art.js | 英雄形象照探測改為並行載入候選圖片,以降低被 SPA fallback 拖慢的機率。 |
| functions/api/og/zg/[[path]].js | 新增 0G Storage 同源代理 Function:indexer 轉發與節點 URL 改寫、node 轉發與白名單限制。 |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+85
to
+87
| const body = | ||
| request.method === 'GET' || request.method === 'HEAD' ? undefined : await request.text(); | ||
| if (body && body.length > MAX_BODY) return json({ error: 'payload too large' }, 413); |
Comment on lines
+387
to
+391
| const results = await Promise.all(HERO_SHEET_CANDIDATES.map(tryLoad)); | ||
| const hit = results.findIndex(Boolean); | ||
| if (hit === -1) return (sheetState = false); | ||
| HERO_SHEET = HERO_SHEET_CANDIDATES[hit]; | ||
| return (sheetState = true); |
Comment on lines
+99
to
+101
| // 這裡刻意忽略呼叫端傳進來的真實 indexer 網址,一律走代理 —— | ||
| // 直連的話 segment 那步會被 storage node 的 CORS 擋掉。 | ||
| const client = new zg.Indexer(new URL(DEFAULT_INDEXER, location.origin).toString()); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
問題定位
診斷收斂後確認:瀏覽器打得到 indexer,卡在後面。
SDK 上傳一個檔案要跟兩種主機講話:
indexer_getShardedNodes)→ 通url直接打每一台 storage node 的zgs_uploadSegment→ 不通那些節點是各自獨立的主機,沒有為瀏覽器開 CORS,axios 只回一句沒有內容的
Network Error。改法
新增
functions/api/og/zg/[[path]].js,把兩步都繞過自家 Worker:瀏覽器全程只跟自己的網域講話,沒有跨來源請求。Flow 合約那步不經過代理(走 MetaMask 簽名,本來就沒 CORS 問題);EVM RPC 維持直連。
節點清單的形狀在 SDK 版本之間變過(有時陣列、有時
{ trusted: [...] }分片物件),所以改寫不假設結構,直接走訪整棵 JSON,看到叫url且是 http(s) 開頭的字串就換掉。非 0g.ai 的主機原樣留著讓它自己失敗,不靜悄悄吞掉。安全性
0g.ai與其子網域的 https 主機 —— 否則就是一個任何人都能借用的開放代理。content-type,不把 cookie、authorization 之類的標頭交給第三方主機。驗證
用假的 indexer / 節點驅動 Function 本體:
wrangler pages dev確認巢狀[[path]]catch-all 路由解析正確、未知路徑回 404,且/api/og/storage與/api/og/status不受影響。0g.ai。失敗的話賽道一與賽道三不受影響。🤖 Generated with Claude Code
https://claude.ai/code/session_015NBxaWtzqEX3i8NvoZEeq3
Generated by Claude Code