fix(servers): 改用 location 取代已被 Hetzner 移除的 datacenter 欄位 - #53
Conversation
Hetzner Cloud API 於 2026-06-30 正式從 Servers 與 Primary IPs 資源移除 `datacenter` 屬性(2025-12-16 公告 "Phasing out Datacenters in favor of Locations",https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters)。 原本 HetznerServerSchema 將 `datacenter` 列為 required,且 formatServer 讀取 `server.datacenter.location.*`,導致移除後所有 hetzner_list_servers / hetzner_get_server / metrics / ssh 相關工具一律拋出: Invalid input: expected object, received undefined (servers.0.datacenter) 改用 API 早已提供的頂層 `location` 物件,且**只宣告 formatServer 實際會渲染的 三個欄位**(name / city / country)。zod 的 z.object 預設就會剝除未宣告的多餘 欄位,因此宣告得越少對上游變更越寬容;反之,一個「宣告為必要卻從未讀取」的欄位 就是一顆定時炸彈——這次的 datacenter 正是如此。 why 不用 .passthrough():實測 zod 4.3.6,預設 z.object 對「多出來的欄位」本來 就 PASS(自動剝除),passthrough 只是改為保留;而兩者對「缺少必要欄位」一律 THROW。也就是說 passthrough 對這次的失效模式毫無防護作用,加了只會誤導。 驗證: - 346 unit tests / typecheck / lint 全綠 - 新增測試以真實 API 形狀(無 datacenter、含 latitude/longitude/network_zone 等未宣告欄位)驗證 schema 可正常解析 - runtime 實打 Hetzner API:hetzner_list_servers 與 hetzner_get_server 均 回傳 "**Location**: Nuremberg, DE (nbg1)"
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
此 PR 針對 Hetzner Cloud API 於 2026-06-30 移除 datacenter 欄位所造成的 server 相關工具全面解析失敗問題,改以頂層 location 欄位為資料來源,恢復 hetzner_list_servers / hetzner_get_server 等工具可正常運作。
Changes:
- 更新
HetznerServerSchema:移除datacenter,改宣告並使用頂層location(僅保留 formatter 會用到的欄位)。 - 更新 servers formatter:
formatServer()改用server.location.*產生 Location 顯示。 - 調整並新增測試:更新 server mock payload,新增 location rendering 與「未知欄位可被容忍」的 regression 測試。
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/types.ts |
將 Server schema 從 datacenter 遷移到頂層 location,避免 Zod 因 required 欄位消失而拋錯 |
src/tools/servers.ts |
formatServer() 改以 server.location 渲染 Location 行 |
tests/tools/servers.test.ts |
更新 mock server shape 並新增 location regression 測試(含 unknown keys 容忍測試) |
tests/tools/server-ssh.test.ts |
更新 mock server payload:以 location 取代 datacenter |
tests/tools/metrics.test.ts |
更新 mock server payload:以 location 取代 datacenter |
Copilot review 指出 baseServer 標註為 HetznerServer,但 location 仍帶著
id / description——schema 已不再宣告這兩個欄位,測試資料因此與 parse 後的實際
形狀不一致。
追查發現這個不一致之所以沒被 typecheck 抓到,是因為 tsconfig.json 的 include
只有 `src/**/*`,測試檔從來就不在 typecheck 範圍內。
fixture 收斂為 { name, country, city }。真實 API 多回傳的欄位
(id / description / latitude / longitude / network_zone)仍由既有的
rawApiServer 測試涵蓋,該測試刻意使用未標註型別的字面值。
註:把 tests/ 納入 typecheck 需另建 tsconfig(現有 rootDir 指向 src,且
tests/api.test.ts、tests/tools/volumes.test.ts 等有 4 個既有型別錯誤),
不在本 hotfix 範圍。
驗證:351 tests / typecheck / lint 全綠。
問題
Hetzner Cloud API 於 2026-06-30 正式從 Servers 與 Primary IPs 資源移除
datacenter屬性(2025-12-16 公告:Phasing out Datacenters in favor of Locations)。HetznerServerSchema將datacenter列為 required,formatServer讀取server.datacenter.location.*,導致所有 server 相關工具一律拋出:影響
hetzner_list_servers/hetzner_get_server/ metrics / ssh 等工具——目前線上就是壞的。修法
改用 API 早已提供的頂層
location物件,且只宣告formatServer實際渲染的三個欄位(name/city/country)。zod 的
z.object預設就會剝除未宣告的多餘欄位,因此宣告得越少對上游變更越寬容;反之,一個「宣告為必要卻從未讀取」的欄位就是定時炸彈——這次的datacenter正是如此。為什麼不用
.passthrough()實測 zod 4.3.6:
z.object.passthrough()passthrough對這次的失效模式毫無防護作用,加了只會誤導。驗證
main上的 security 測試)grep -rn '\.datacenter' src/→ 無殘留datacenter、含latitude/longitude/network_zone等未宣告欄位)驗證 schema 可正常解析hetzner_list_servers回傳isError: false與**Location**: Nuremberg, DE (nbg1)Test plan
bun run test(351 passed)bun run typecheckbun run linthetzner_list_servers/hetzner_get_serverdevelop落後main16 個 commit,且兩者在src/tools/server-ssh.ts、src/tools/storage-boxes.ts、src/utils.ts有 18 個衝突區塊(SSH host-key pinning、path-traversal 防護 vs storage-boxes 功能)。需要另外處理,否則下一個走develop的 PR 都會撞到同樣問題。