You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug] Gui shows "Full config PUT is disabled. Use /api/providers POST for provider changes." after editing providers JSON and clicking "save" #3280
Providers 화면의 Edit JSON 저장이 지금 dev HEAD(410a48a4f, package 2.41.0)에서 의도적으로 막혀 있는 PUT /api/config를 그대로 호출하고 있습니다. 제보 버전은 2.40.0이고, 콘솔에 PUT http://localhost:10100/api/config 405가 찍힌 뒤 서버가 돌려주는 문구 그대로 토스트에 뜹니다. 즉 프록시가 죽은 게 아니라, GUI 저장 경로와 서버 정책이 어긋난 상태입니다.
서버 쪽은 src/server/management/config-routes.ts에서 PUT /api/config를 받으면 항상 405와 함께 Full config PUT is disabled. Use /api/providers POST for provider changes.를 돌려줍니다. src/server/management/route-registry.ts에도 이 PUT이 exempt: disabled로 등록되어 있고, 이유에 provider 변경은 POST /api/providers로 가라고 적혀 있습니다. 예전 full-config PUT은 GET으로 받은 마스킹된 키를 다시 저장하거나, 오래된 스냅샷으로 이미 지운 provider/custom model을 살려 버리는 문제가 있었습니다(관련 닫힌 이슈 #1273). 그래서 서버가 막을 이유는 분명합니다.
그런데 GUI는 아직 예전 경로를 씁니다. gui/src/hooks/useJsonConfigEditor.ts의 saveConfig가 draft JSON을 파싱한 뒤 apiBase + /api/config`` 경로에 PUT만 보냅니다. 이 훅은 gui/src/pages/Providers.tsx에서 쓰이고, Providers 개요의 Edit JSON 버튼(`gui/src/components/provider-workspace/ProviderOverviewDashboard.tsx`, i18n 키 `prov.editJson`)이 그 에디터를 엽니다. 사용자가 JSON을 고치고 Save를 누르면 서버는 설계대로 405를 주고, GUI는 `data.error`를 실패 알림으로 보여 줍니다. 제보 스크린샷/로그와 코드가 한 줄로 이어집니다.
올바른 쓰기 API는 provider 단위입니다. src/server/management/provider-routes.ts의 POST /api/providers는 이름과 provider 객체(그리고 선택적 setDefault)로 한 provider를 merge·persist하고, 마스킹된 키를 통째로 다시 쓰지 않도록 기존 pool/costs/failover 등을 보존합니다. PATCH/DELETE도 같은 모듈에 있습니다. 반면 Edit JSON 초안은 safeConfigDTO 모양의 전체 config(port, defaultProvider, providers 맵 등)라서, 그대로 POST 한 방으로 치환할 수 없습니다. 그래서 고칠 때는 (1) Edit JSON을 끄거나 안내만 남기거나, (2) providers diff를 계산해 POST/PATCH/DELETE로 쪼개 보내거나, (3) top-level 설정은 PUT /api/settings 등 허용된 부분 API로 나누는 설계가 필요합니다. types.ts/config.ts split 캠페인과 겹치는 PR은 아니고, 중복 이슈로 바로 닫을 만한 열린 twin도 지금은 #3280이 유일해 보입니다.
재현은 Windows 10 + dashboard Providers → Edit JSON → Save이며, 서버 메시지와 405만으로도 HEAD에서 재현 가능성이 높습니다. Cursor Private Inference / effort-table 스택(#3273/#3275 및 열린 #3276–#3278)과는 무관한 GUI/management 버그라서 그 merge train을 막지는 않습니다. 다만 Providers JSON 편집은 운영자가 자주 쓰는 표면이라, 문서에 PUT 금지만 있고 UI는 그대로면 같은 문의가 반복됩니다.
gui/src/hooks/useJsonConfigEditor.ts 라인 35-38 - Save가 여전히 PUT /api/config만 호출해서 서버 405 정책과 정면으로 충돌한다 src/server/management/config-routes.ts 라인 252-253 - full config PUT은 의도적 405이며, 에러 문구가 GUI 토스트에 그대로 노출된다 src/server/management/route-registry.ts 라인 165 - PUT /api/config가 disabled로 등록돼 있어 서버를 다시 여는 방향은 기본 추천이 아니다
경로 gui/src/pages/Providers.tsx / ProviderOverviewDashboard.tsx - Edit JSON 진입점은 살아 있는데 저장 API만 죽었다
심볼 POST /api/providers - 서버가 가리키는 대체 API는 provider 단위라서, 전체 JSON 초안을 한 번에 넣을 계약이 아니다
메인테이너의 판단이 필요한 지점
Edit JSON 기능을 유지할지, Providers에서는 form/POST·PATCH만 남기고 raw JSON 편집을 내릴지
유지한다면 providers 맵 diff → POST/PATCH/DELETE 오케스트레이션만 허용할지, top-level(port 등)까지 부분 API로 같이 살릴지
보안/정합성 이유로 full PUT을 예외적으로 되살리는 옵션을 절대 주지 않을지(과거 #1273 부활 위험)
너의 추천 dev 기준 서버 정책은 유지하고, GUI useJsonConfigEditor.saveConfig를 고치는 작은 버그 PR을 받는 쪽이 맞다. 최소 수정은 Save 시 providers만 diff해서 POST/PATCH/DELETE /api/providers로 보내고, 전체 PUT이 필요한 편집이면 미리 막거나 안내하는 것이다. full PUT /api/config를 다시 여는 것은 #1273 재발을 부르니 비추천. 라벨 bug/gui 유지는 맞고, 수정 PR이 오면 이 이슈에 묶어 닫으면 된다.
The editor was serializing the redacted config DTO and PUT-ing it to /api/config, which the server rejects on purpose. The obvious workaround — fanning the edit out to per-provider POST/PATCH/DELETE — would have been worse: each of those persists independently, so a mid-sequence failure leaves half the edit on disk and every field missing from the public DTO is lost on the way back.
So the write stays server-side. PUT /api/providers takes { baseline, next }: the dashboard sends only what it can see, the server compares the baseline against the current public projection, merges into freshly read persisted providers while keeping API keys, pools and headers, and commits once. A stale baseline is a 409 rather than a silent overwrite.
One thing worth recording, since it nearly shipped: the first implementation used an allowlist of editable fields, and testing it in a real browser rejected an untouched config with provider "woong" contains non-editable field "note" — trading a clear error for a save that refuses your own config. The policy is now an exhaustive per-field classification that tsc enforces, so a new provider field cannot be silently omitted from either the projection or the write denylist.
Verified end-to-end in the dashboard rather than only in tests. Editing a provider's note and clicking Save now reports Saved! Restart proxy to apply., and the persisted config afterwards showed the edit applied, both providers' API keys intact through a round trip that never carried them, and no hasApiKey-style derived marker written back as data.
Activity
lidge-jun commentedon Sep 2, 2026
리뷰 · 우선순위 58 / 80
Providers 화면의 Edit JSON 저장이 지금
devHEAD(410a48a4f, package2.41.0)에서 의도적으로 막혀 있는PUT /api/config를 그대로 호출하고 있습니다. 제보 버전은2.40.0이고, 콘솔에PUT http://localhost:10100/api/config 405가 찍힌 뒤 서버가 돌려주는 문구 그대로 토스트에 뜹니다. 즉 프록시가 죽은 게 아니라, GUI 저장 경로와 서버 정책이 어긋난 상태입니다.서버 쪽은
src/server/management/config-routes.ts에서PUT /api/config를 받으면 항상 405와 함께Full config PUT is disabled. Use /api/providers POST for provider changes.를 돌려줍니다.src/server/management/route-registry.ts에도 이 PUT이exempt: disabled로 등록되어 있고, 이유에 provider 변경은POST /api/providers로 가라고 적혀 있습니다. 예전 full-config PUT은 GET으로 받은 마스킹된 키를 다시 저장하거나, 오래된 스냅샷으로 이미 지운 provider/custom model을 살려 버리는 문제가 있었습니다(관련 닫힌 이슈#1273). 그래서 서버가 막을 이유는 분명합니다.그런데 GUI는 아직 예전 경로를 씁니다.
gui/src/hooks/useJsonConfigEditor.ts의saveConfig가 draft JSON을 파싱한 뒤apiBase +/api/config`` 경로에 PUT만 보냅니다. 이 훅은gui/src/pages/Providers.tsx에서 쓰이고, Providers 개요의 Edit JSON 버튼(`gui/src/components/provider-workspace/ProviderOverviewDashboard.tsx`, i18n 키 `prov.editJson`)이 그 에디터를 엽니다. 사용자가 JSON을 고치고 Save를 누르면 서버는 설계대로 405를 주고, GUI는 `data.error`를 실패 알림으로 보여 줍니다. 제보 스크린샷/로그와 코드가 한 줄로 이어집니다.올바른 쓰기 API는 provider 단위입니다.
src/server/management/provider-routes.ts의POST /api/providers는 이름과 provider 객체(그리고 선택적 setDefault)로 한 provider를 merge·persist하고, 마스킹된 키를 통째로 다시 쓰지 않도록 기존 pool/costs/failover 등을 보존합니다.PATCH/DELETE도 같은 모듈에 있습니다. 반면 Edit JSON 초안은safeConfigDTO모양의 전체 config(port, defaultProvider, providers 맵 등)라서, 그대로 POST 한 방으로 치환할 수 없습니다. 그래서 고칠 때는 (1) Edit JSON을 끄거나 안내만 남기거나, (2) providers diff를 계산해 POST/PATCH/DELETE로 쪼개 보내거나, (3) top-level 설정은PUT /api/settings등 허용된 부분 API로 나누는 설계가 필요합니다. types.ts/config.ts split 캠페인과 겹치는 PR은 아니고, 중복 이슈로 바로 닫을 만한 열린 twin도 지금은#3280이 유일해 보입니다.재현은 Windows 10 + dashboard Providers → Edit JSON → Save이며, 서버 메시지와 405만으로도 HEAD에서 재현 가능성이 높습니다. Cursor Private Inference / effort-table 스택(
#3273/#3275및 열린#3276–#3278)과는 무관한 GUI/management 버그라서 그 merge train을 막지는 않습니다. 다만 Providers JSON 편집은 운영자가 자주 쓰는 표면이라, 문서에 PUT 금지만 있고 UI는 그대로면 같은 문의가 반복됩니다.gui/src/hooks/useJsonConfigEditor.ts라인 35-38 - Save가 여전히PUT /api/config만 호출해서 서버 405 정책과 정면으로 충돌한다src/server/management/config-routes.ts라인 252-253 - full config PUT은 의도적 405이며, 에러 문구가 GUI 토스트에 그대로 노출된다src/server/management/route-registry.ts라인 165 - PUT /api/config가 disabled로 등록돼 있어 서버를 다시 여는 방향은 기본 추천이 아니다경로
gui/src/pages/Providers.tsx/ProviderOverviewDashboard.tsx- Edit JSON 진입점은 살아 있는데 저장 API만 죽었다심볼
POST /api/providers- 서버가 가리키는 대체 API는 provider 단위라서, 전체 JSON 초안을 한 번에 넣을 계약이 아니다메인테이너의 판단이 필요한 지점
#1273부활 위험)너의 추천
dev기준 서버 정책은 유지하고, GUIuseJsonConfigEditor.saveConfig를 고치는 작은 버그 PR을 받는 쪽이 맞다. 최소 수정은 Save 시 providers만 diff해서POST/PATCH/DELETE /api/providers로 보내고, 전체 PUT이 필요한 편집이면 미리 막거나 안내하는 것이다. fullPUT /api/config를 다시 여는 것은#1273재발을 부르니 비추천. 라벨bug/gui유지는 맞고, 수정 PR이 오면 이 이슈에 묶어 닫으면 된다.이 댓글은 grok-bot이 작성했습니다
fix(providers): save the dashboard provider editor atomically (#3296)
lidge-jun commentedon Sep 2, 2026
Fixed on
devin 3c7c021 (#3296).The editor was serializing the redacted config DTO and
PUT-ing it to/api/config, which the server rejects on purpose. The obvious workaround — fanning the edit out to per-providerPOST/PATCH/DELETE— would have been worse: each of those persists independently, so a mid-sequence failure leaves half the edit on disk and every field missing from the public DTO is lost on the way back.So the write stays server-side.
PUT /api/providerstakes{ baseline, next }: the dashboard sends only what it can see, the server compares the baseline against the current public projection, merges into freshly read persisted providers while keeping API keys, pools and headers, and commits once. A stale baseline is a 409 rather than a silent overwrite.One thing worth recording, since it nearly shipped: the first implementation used an allowlist of editable fields, and testing it in a real browser rejected an untouched config with
provider "woong" contains non-editable field "note"— trading a clear error for a save that refuses your own config. The policy is now an exhaustive per-field classification thattscenforces, so a new provider field cannot be silently omitted from either the projection or the write denylist.Verified end-to-end in the dashboard rather than only in tests. Editing a provider's
noteand clicking Save now reportsSaved! Restart proxy to apply., and the persisted config afterwards showed the edit applied, both providers' API keys intact through a round trip that never carried them, and nohasApiKey-style derived marker written back as data.fix(providers): save the dashboard provider editor atomically (lidge-…
fix(providers): save the dashboard provider editor atomically (lidge-…