refactor(settings-panel): migrate 15 bare fetch to apiFetch#686
Open
jun261930-tech wants to merge 2 commits into
Open
refactor(settings-panel): migrate 15 bare fetch to apiFetch#686jun261930-tech wants to merge 2 commits into
jun261930-tech wants to merge 2 commits into
Conversation
P1 of MC code-quality plan (see PLAN.md / PR-api-client.md):
- src/lib/api-client.ts (+120) ApiError + apiFetch<T> wrapper
- src/lib/__tests__/api-client.test.ts (+108) 8 vitest cases (200/401/403/500/network/loop/204/no-redirect)
- src/components/auth-expired-listener.tsx (+30) global mc:auth-expired -> /login redirect
- src/app/layout.tsx (+2) register listener
- eslint.config.mjs (+31) no-restricted-syntax warn for bare fetch('/api/...')
Quality gates passed:
- pnpm vitest run src/lib/__tests__/api-client.test.ts: 8/8 (2.5s)
- pnpm typecheck: 0 error
- pnpm lint: 0 error, 343 warn (~baseline 334 bare fetch + 9 pre-existing)
Out of scope: migrating existing 334 bare fetch sites (P2, separate PR).
P0 verification: playwright captured 19/19 /api/* returning 200 after login,
ruling out backend issue (cookie expired) — see p0-network-evidence.txt.
Migrate all fetch('/api/...') calls in settings-panel.tsx to apiFetch from
@/lib/api-client. Adopts the global 401/403/5xx interceptor for unified
auth-expired handling across the panel:
- fetchSettings (3 inner): /api/settings + /api/agents + /api/sessions
- fetchApiKeyInfo: /api/tokens/rotate (GET)
- handleRotateKey: /api/tokens/rotate (POST)
- fetchHermesStatus: /api/hermes (GET)
- handleSave: /api/settings (PUT)
- handleReset: /api/settings (DELETE template literal)
- MC backup, gateway backup: /api/backup (POST)
- Onboarding reset: /api/onboarding (POST)
- Hermes hook install/uninstall: /api/hermes (POST)
- Hook profile selector: /api/settings (POST)
- InterfaceModeSelector: /api/settings (PUT, fire-and-forget)
- AccountOAuthSection.handleDisconnect: /api/auth/google/disconnect (POST)
Pattern follows established conventions:
- GET with auto-parse: apiFetch<T>(url)
- POST/PUT/DELETE keeping res.ok semantics: apiFetch<Response>(url, {
method, headers, body, raw: true })
- The /api/settings GET keeps redirectOnUnauthenticated: false because
the panel manually issues window.location.assign('/login?next=…')
Verification:
- pnpm typecheck: 0 errors
- pnpm lint: 0 errors, 328 warnings (343 -> 328, -15 matches migrated count)
- api-client unit tests: 8/8 pass
Refs PR builderz-labs#681 (api-client base)
This was referenced May 17, 2026
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.
Summary
Migrate
src/components/panels/settings-panel.tsx(the largest single-file fetch hotspot, 15 bare fetch calls) toapiFetch<T>()from@/lib/api-client.Calls Migrated (15 total)
Settings core (CRUD):
GET /api/settings(load) — keepsredirectOnUnauthenticated: falsebecause the panel manually redirects to/login?next=%2FsettingsPUT /api/settings(save changes)DELETE /api/settings?key=…(reset to default, template literal)POST /api/settings(hook profile selector)PUT /api/settings(interface-mode switcher, fire-and-forget)Coordinator routing preview (nested in fetchSettings):
GET /api/agents?limit=200GET /api/sessionsAPI key management:
GET /api/tokens/rotate(info)POST /api/tokens/rotate(rotate)Hermes integration:
GET /api/hermes(status)POST /api/hermes(install / uninstall hook)Backups:
POST /api/backup(MC database)POST /api/backup?target=gateway(gateway state)Onboarding & OAuth:
POST /api/onboarding(replay reset)POST /api/auth/google/disconnect(disconnect Google)Migration Pattern
apiFetch<T>(url)res.oksemantics:apiFetch<Response>(url, { method, headers, body, raw: true })if (res.ok) { const data = await res.json() … }pattern where status code wasn't needed (tokens info, hermes status, agents/sessions)Verification
pnpm typecheck→ 0 errorpnpm lint→ 0 error, 328 warnings (baseline 343 → 328, -15 matches migrated count)vitest run src/lib/__tests__/api-client.test.ts→ 8/8 passWhy this matters
settings-panel.tsxwas the single largest concentration of bare fetch (15 calls). After this PR it adopts the same global 401/403/5xx interceptor as cost-tracker, dashboard, chat, and panel-route, completing the P2 Top-5 hotspot migration.Refs PR #681 (api-client base)
Closes: task-11 P2.5 (settings-panel hotspot)