refactor(web): consolidate API layer onto @cipherbox/api-client#309
Conversation
… to instance - Add refreshAccessToken, withCredentials, and onRefreshFailure to ApiClientConfig - Add shared-promise 401 response interceptor to getCachedInstance() - Deduplicate concurrent refresh requests with same pattern as web's client.ts - Skip retry for /auth/refresh URL to prevent infinite loops Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 6138361d088c
- Create lib/api-config.ts for early setApiClientConfig() at module load - Import api-config in main.tsx before any auth API calls - Rewrite lib/api/auth.ts as thin adapter delegating to api-client - Rewrite lib/api/vault.ts as thin adapter delegating to api-client - Rewrite lib/api/ipfs.ts with progress/cancel support via api-client - Create hooks/useHealthCheck.ts replacing orval react-query hook - Update all 11 consumer files to import from @cipherbox/api-client - Remove redundant setApiClientConfig call in useAuth.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: f6790debadb5
- Remove entire apps/web/src/api/ directory (orval-generated react-query hooks) - Remove apps/web/orval.config.ts (no longer needed) - Remove apps/web/src/lib/api/client.ts (replaced by api-client instance) - Remove apps/web/src/lib/api/__tests__/client-refresh.test.ts (tested at api-client level) - Remove orval and axios-mock-adapter from web devDependencies - Remove api:generate script from web package.json - Update root api:generate to use @cipherbox/api-client generate + build - Update CLAUDE.md to reference packages/api-client paths - Add backward-compatible QuotaResponse/VaultResponse type aliases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WalkthroughCentralizes API client generation into Changes
Sequence Diagram(s)sequenceDiagram
participant UI as "UI Component"
participant ApiPkg as "@cipherbox/api-client"
participant ApiConfig as "apps/web/lib/api-config (module)"
participant AuthStore as "Auth Store"
participant Server as "API Server"
UI->>ApiPkg: call controller (e.g., GET /resource)
ApiPkg-->>Server: HTTP request
Server-->>ApiPkg: 401 Unauthorized
ApiPkg->>ApiConfig: trigger refresh via authControllerRefresh
ApiConfig->>Server: POST /auth/refresh
Server-->>ApiConfig: 200 { accessToken }
ApiConfig->>AuthStore: set accessToken, set authenticated
ApiConfig->>ApiPkg: subsequent requests include new token
ApiPkg->>Server: retry original request with new token
Server-->>ApiPkg: 200 OK
ApiPkg-->>UI: return data
alt refresh fails
ApiConfig->>AuthStore: clear stores / logout
ApiPkg-->>UI: propagate error
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/web/src/lib/api-config.ts (1)
32-37: Consider adding error handling for the dynamic import inonRefreshFailure.The
import().then()pattern could fail silently if the module fails to load. While rare, an unhandled rejection here would leave the user in an inconsistent state (refresh failed, but stores not cleared).♻️ Add catch handler
onRefreshFailure: () => { // Dynamic import to avoid circular dependency at module load time import('../lib/clear-user-stores').then(({ clearAllUserStores }) => { clearAllUserStores(); - }); + }).catch((err) => { + console.error('[api-config] Failed to clear user stores on refresh failure:', err); + }); },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/lib/api-config.ts` around lines 32 - 37, The onRefreshFailure handler uses a dynamic import of '../lib/clear-user-stores' and then calls clearAllUserStores but has no error handling; wrap the import().then(...) with a .catch that logs the import failure (e.g., via console.error or your logger) and attempts a safe fallback to still clear stores (for example, call a local safeClearAllUserStores routine or try a synchronous require('../lib/clear-user-stores') and call clearAllUserStores if available). Ensure the handler references onRefreshFailure and clearAllUserStores so failures won't leave stores uncleared and the rejection is not silent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/src/lib/api/auth.ts`:
- Around line 67-76: Remove the unsafe `as` casts and use the generated DTO
types directly: in linkMethod stop casting data.loginType to
LinkMethodDtoLoginType and pass the union value (it already matches
LinkMethodDtoLoginType) so the call to authControllerLinkMethod uses the
generated enum; for identityControllerSendOtp and
identityControllerGetWalletNonce stop casting the returned shapes and instead
annotate/return the functions with the generated response types
SendOtpResponseDto and IdentityControllerGetWalletNonce200 (or accept those
promise results directly) so the adapter surface types match the
packages/api-client DTOs.
In `@apps/web/src/lib/api/ipfs.ts`:
- Around line 72-81: The progress-enabled download branch uses raw fetch (in
getApiClientConfig()/the file's fetch block) so it bypasses the shared axios
client and its refresh-and-retry interceptor (see the interceptor in
packages/api-client/src/instance.ts), causing failed 401s to never retry; fix by
routing the onProgress branch through the shared API client or by implementing
the same refresh-and-retry logic locally: use the axios instance from
`@cipherbox/api-client` (or call its request method) to perform the download
request so the interceptor can refresh tokens, and if you must use fetch because
of streaming, replicate the interceptor behavior by attempting a token refresh
and retrying the request on 401 (use the same token-refresh helper used by the
shared client) and ensure Authorization header is set from
getApiClientConfig().getAccessToken() before retrying.
---
Nitpick comments:
In `@apps/web/src/lib/api-config.ts`:
- Around line 32-37: The onRefreshFailure handler uses a dynamic import of
'../lib/clear-user-stores' and then calls clearAllUserStores but has no error
handling; wrap the import().then(...) with a .catch that logs the import failure
(e.g., via console.error or your logger) and attempts a safe fallback to still
clear stores (for example, call a local safeClearAllUserStores routine or try a
synchronous require('../lib/clear-user-stores') and call clearAllUserStores if
available). Ensure the handler references onRefreshFailure and
clearAllUserStores so failures won't leave stores uncleared and the rejection is
not silent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ce8ee71c-43a7-45b6-9cac-5a9c12b37327
⛔ Files ignored due to path filters (122)
apps/web/src/api/auth/auth.tsis excluded by!apps/web/src/api/**apps/web/src/api/custom-instance.tsis excluded by!apps/web/src/api/**apps/web/src/api/device-approval/device-approval.tsis excluded by!apps/web/src/api/**apps/web/src/api/health/health.tsis excluded by!apps/web/src/api/**apps/web/src/api/identity/identity.tsis excluded by!apps/web/src/api/**apps/web/src/api/invites/invites.tsis excluded by!apps/web/src/api/**apps/web/src/api/ipfs/ipfs.tsis excluded by!apps/web/src/api/**apps/web/src/api/ipns/ipns.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/addShareKeysDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/authMethodResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/authMethodResponseDtoType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/batchPublishIpnsDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/batchPublishIpnsResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/childKeyDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/childKeyDtoKeyType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/claimChildKeyDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/claimChildKeyDtoKeyType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/claimInviteDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/claimInviteResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/createApprovalDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/createInviteDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/createInviteDtoItemType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/createShareDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/createShareDtoItemType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/createShareResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/createShareResponseDtoItemType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/deleteAccountDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/deleteAccountResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/derivationInfoDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/derivationInfoDtoDerivationVersion.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/derivationInfoDtoMethod.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/desktopRefreshDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/deviceApprovalControllerCreateRequest201.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/deviceApprovalControllerGetPending200Item.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/deviceApprovalControllerGetStatus200.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/deviceApprovalControllerGetStatus200Status.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/googleLoginDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/googleLoginDtoIntent.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/healthControllerCheck200.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/healthControllerCheck200Info.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/healthControllerCheck200InfoDatabase.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/identityControllerGetJwks200.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/identityControllerGetJwks200KeysItem.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/identityControllerGetWalletNonce200.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/identityTokenResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/index.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/initVaultDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteChildKeyDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteChildKeyDtoKeyType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteDataResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteDataResponseDtoItemType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteDataResponseDtoStatus.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteResponseDtoItemType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteResponseDtoStatus.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteStatusResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/inviteStatusResponseDtoStatus.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/ipfsControllerUploadBody.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/ipnsControllerResolveRecordParams.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/linkMethodDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/linkMethodDtoLoginType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/loginDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/loginDtoLoginType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/loginResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/logoutResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/lookupUserResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/paginatedReceivedSharesDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/paginatedSentSharesDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/pendingRotationResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/pendingRotationResponseDtoItemType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/publishIpnsDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/publishIpnsEntryDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/publishIpnsEntryDtoRecordType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/publishIpnsResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/quotaResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/receivedShareResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/receivedShareResponseDtoItemType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/refreshDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/resolveIpnsResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/respondApprovalDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/respondApprovalDtoAction.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/sendOtpDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/sendOtpResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/sentShareResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/sentShareResponseDtoItemType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/shareInvitesControllerListInvitesParams.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/shareKeyEntryDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/shareKeyEntryDtoKeyType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/shareKeyResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/shareKeyResponseDtoKeyType.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/sharesControllerGetReceivedSharesParams.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/sharesControllerGetSentSharesParams.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/sharesControllerLookupUserParams.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/teeKeysDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/teeKeysDtoPreviousEpoch.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/teeKeysDtoPreviousPublicKey.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/testLoginDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/testLoginResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/tokenResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/unlinkMethodDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/unlinkMethodResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/unpinDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/unpinResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/updateEncryptedKeyDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/uploadResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/vaultConfigResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/vaultExportDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/vaultExportDtoDerivationInfo.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/vaultExportDtoDerivationMethod.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/vaultResponseDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/vaultResponseDtoInitializedAt.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/vaultResponseDtoTeeKeys.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/verifyOtpDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/verifyOtpDtoIntent.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/walletVerifyDto.tsis excluded by!apps/web/src/api/**apps/web/src/api/models/walletVerifyDtoIntent.tsis excluded by!apps/web/src/api/**apps/web/src/api/root/root.tsis excluded by!apps/web/src/api/**apps/web/src/api/share-invites/share-invites.tsis excluded by!apps/web/src/api/**apps/web/src/api/shares/shares.tsis excluded by!apps/web/src/api/**apps/web/src/api/vault/vault.tsis excluded by!apps/web/src/api/**packages/api-client/src/instance.tsis excluded by!packages/api-client/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!**/pnpm-lock.yaml
📒 Files selected for processing (23)
.claude/claude.mdapps/web/orval.config.tsapps/web/package.jsonapps/web/scripts/fix-orval-imports.mjsapps/web/src/components/file-browser/ShareDialog.tsxapps/web/src/components/layout/StatusIndicator.tsxapps/web/src/components/vault/VaultExport.tsxapps/web/src/hooks/useAuth.tsapps/web/src/hooks/useHealthCheck.tsapps/web/src/lib/api-config.tsapps/web/src/lib/api/__tests__/client-refresh.test.tsapps/web/src/lib/api/auth.tsapps/web/src/lib/api/client.tsapps/web/src/lib/api/ipfs.tsapps/web/src/lib/api/vault.tsapps/web/src/lib/crypto/key-wrapping.tsapps/web/src/main.tsxapps/web/src/routes/Login.tsxapps/web/src/services/device-approval.service.tsapps/web/src/services/invite.service.tsapps/web/src/services/ipns.service.tsapps/web/src/services/share.service.tspackage.json
💤 Files with no reviewable changes (4)
- apps/web/scripts/fix-orval-imports.mjs
- apps/web/src/lib/api/tests/client-refresh.test.ts
- apps/web/orval.config.ts
- apps/web/src/lib/api/client.ts
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #309 +/- ##
==========================================
+ Coverage 49.83% 50.28% +0.44%
==========================================
Files 114 115 +1
Lines 9126 9854 +728
Branches 699 716 +17
==========================================
+ Hits 4548 4955 +407
- Misses 4402 4722 +320
- Partials 176 177 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
The web app no longer has its own orval-generated API client — it uses @cipherbox/api-client directly. The CI verify step was failing because the directory no longer exists. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 8e48c657e32a
…ownload - Remove `as` casts in auth.ts adapter; use generated DTO types directly (SendOtpResponseDto, IdentityControllerGetWalletNonce200, LinkMethodDto) - Add refresh-and-retry to the fetch-based download path in ipfs.ts so progress-enabled downloads don't bypass the shared 401 refresh flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 721639822c83
Route all IPFS downloads through ipfsControllerGet with onDownloadProgress — same pattern as uploads. Eliminates the raw fetch path that bypassed the shared axios instance and its 401 refresh interceptor. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 09148ff766df
There was a problem hiding this comment.
Pull request overview
Consolidates the web app’s API access onto the shared @cipherbox/api-client package, removing the legacy web-local orval client and adding shared refresh-on-401 handling in the api-client instance layer to keep sessions alive.
Changes:
- Added configurable 401 refresh+retry interceptor (with shared refresh promise deduplication) to
@cipherbox/api-client. - Replaced web-local generated/hand-rolled clients with thin adapters + early api-client configuration, and migrated imports across the web app.
- Removed orval tooling and ~all of
apps/web/src/api/*(generated client + models), updating CI generation checks accordingly.
Reviewed changes
Copilot reviewed 145 out of 146 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Removes web-only dev deps tied to orval + axios-mock-adapter. |
| packages/api-client/src/instance.ts | Adds refresh-on-401 interceptor, withCredentials support, and refresh failure callback. |
| package.json | Updates api:generate to regenerate/build @cipherbox/api-client instead of the web orval client. |
| apps/web/src/services/share.service.ts | Switches shares API imports to @cipherbox/api-client. |
| apps/web/src/services/ipns.service.ts | Switches IPNS API imports/types to @cipherbox/api-client. |
| apps/web/src/services/invite.service.ts | Switches invites/share-invites API imports to @cipherbox/api-client. |
| apps/web/src/services/device-approval.service.ts | Switches device approval API imports/types to @cipherbox/api-client with local type aliases. |
| apps/web/src/routes/Login.tsx | Replaces orval react-query hook with useHealthCheck. |
| apps/web/src/main.tsx | Ensures early api-client configuration via module import. |
| apps/web/src/lib/crypto/key-wrapping.ts | Moves DTO type import to @cipherbox/api-client. |
| apps/web/src/lib/api/vault.ts | Rewrites vault API access as thin adapter over @cipherbox/api-client. |
| apps/web/src/lib/api/ipfs.ts | Rewrites IPFS API access over @cipherbox/api-client, keeping upload progress + streaming download option. |
| apps/web/src/lib/api/client.ts | Deletes legacy axios client and its refresh interceptor. |
| apps/web/src/lib/api/auth.ts | Rewrites auth API access as thin adapter over @cipherbox/api-client. |
| apps/web/src/lib/api/tests/client-refresh.test.ts | Removes tests that covered refresh deduplication behavior in the web-local client. |
| apps/web/src/lib/api-config.ts | Adds early setApiClientConfig with refresh + cookie support for web. |
| apps/web/src/hooks/useHealthCheck.ts | New react-query wrapper hook for health checks using @cipherbox/api-client. |
| apps/web/src/hooks/useAuth.ts | Removes direct setApiClientConfig usage; relies on lib/api-config.ts. |
| apps/web/src/components/vault/VaultExport.tsx | Switches vault export API import to @cipherbox/api-client. |
| apps/web/src/components/layout/StatusIndicator.tsx | Replaces orval health hook usage with useHealthCheck. |
| apps/web/src/components/file-browser/ShareDialog.tsx | Switches shares API imports and DTO types to @cipherbox/api-client. |
| apps/web/src/api/vault/vault.ts | Deletes web-local orval-generated vault client. |
| apps/web/src/api/share-invites/share-invites.ts | Deletes web-local orval-generated share-invites client. |
| apps/web/src/api/root/root.ts | Deletes web-local orval-generated root client. |
| apps/web/src/api/models/walletVerifyDtoIntent.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/walletVerifyDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/verifyOtpDtoIntent.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/verifyOtpDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/vaultResponseDtoTeeKeys.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/vaultResponseDtoInitializedAt.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/vaultResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/vaultExportDtoDerivationMethod.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/vaultExportDtoDerivationInfo.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/vaultExportDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/vaultConfigResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/uploadResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/updateEncryptedKeyDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/unpinResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/unpinDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/unlinkMethodResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/unlinkMethodDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/tokenResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/testLoginResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/testLoginDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/teeKeysDtoPreviousPublicKey.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/teeKeysDtoPreviousEpoch.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/teeKeysDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/sharesControllerLookupUserParams.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/sharesControllerGetSentSharesParams.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/sharesControllerGetReceivedSharesParams.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/shareKeyResponseDtoKeyType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/shareKeyResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/shareKeyEntryDtoKeyType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/shareKeyEntryDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/shareInvitesControllerListInvitesParams.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/sentShareResponseDtoItemType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/sentShareResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/sendOtpResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/sendOtpDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/respondApprovalDtoAction.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/respondApprovalDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/resolveIpnsResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/refreshDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/receivedShareResponseDtoItemType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/receivedShareResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/quotaResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/publishIpnsResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/publishIpnsEntryDtoRecordType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/publishIpnsEntryDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/publishIpnsDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/pendingRotationResponseDtoItemType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/pendingRotationResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/paginatedSentSharesDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/paginatedReceivedSharesDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/lookupUserResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/logoutResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/loginResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/loginDtoLoginType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/loginDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/linkMethodDtoLoginType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/linkMethodDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/ipnsControllerResolveRecordParams.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/ipfsControllerUploadBody.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteStatusResponseDtoStatus.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteStatusResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteResponseDtoStatus.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteResponseDtoItemType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteDataResponseDtoStatus.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteDataResponseDtoItemType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteDataResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteChildKeyDtoKeyType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/inviteChildKeyDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/initVaultDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/index.ts | Deletes web-local orval-generated model barrel; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/identityTokenResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/identityControllerGetWalletNonce200.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/identityControllerGetJwks200KeysItem.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/identityControllerGetJwks200.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/healthControllerCheck200InfoDatabase.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/healthControllerCheck200Info.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/healthControllerCheck200.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/googleLoginDtoIntent.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/googleLoginDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/deviceApprovalControllerGetStatus200Status.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/deviceApprovalControllerGetStatus200.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/deviceApprovalControllerGetPending200Item.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/deviceApprovalControllerCreateRequest201.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/desktopRefreshDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/derivationInfoDtoMethod.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/derivationInfoDtoDerivationVersion.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/derivationInfoDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/deleteAccountResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/deleteAccountDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/createShareResponseDtoItemType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/createShareResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/createShareDtoItemType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/createShareDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/createInviteDtoItemType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/createInviteDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/createApprovalDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/claimInviteResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/claimInviteDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/claimChildKeyDtoKeyType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/claimChildKeyDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/childKeyDtoKeyType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/childKeyDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/batchPublishIpnsResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/batchPublishIpnsDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/authMethodResponseDtoType.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/authMethodResponseDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/models/addShareKeysDto.ts | Deletes web-local orval-generated model; now sourced from @cipherbox/api-client. |
| apps/web/src/api/ipns/ipns.ts | Deletes web-local orval-generated IPNS client. |
| apps/web/src/api/ipfs/ipfs.ts | Deletes web-local orval-generated IPFS client. |
| apps/web/src/api/invites/invites.ts | Deletes web-local orval-generated invites client. |
| apps/web/src/api/identity/identity.ts | Deletes web-local orval-generated identity client. |
| apps/web/src/api/health/health.ts | Deletes web-local orval-generated health client. |
| apps/web/src/api/device-approval/device-approval.ts | Deletes web-local orval-generated device-approval client. |
| apps/web/src/api/custom-instance.ts | Deletes web-local fetch-based mutator used by orval client. |
| apps/web/src/api/auth/auth.ts | Deletes web-local orval-generated auth client. |
| apps/web/scripts/fix-orval-imports.mjs | Removes orval post-processing script (no longer needed). |
| apps/web/package.json | Removes orval generation script + dev deps; retains tests/lint/build. |
| apps/web/orval.config.ts | Deletes web-local orval config. |
| .github/workflows/ci.yml | Updates generated-file formatting/diff checks to only track packages/api-client/. |
| .claude/claude.md | Updates contributor guidance to commit generated api-client outputs from packages/api-client. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
- auth.ts: use generated DTO return types instead of Promise<unknown> - useHealthCheck: use Error instead of void for TError generic - api-config.ts: add .catch() to dynamic import in onRefreshFailure - instance.ts: apply withCredentials in createAxiosInstance, use safe header['Authorization'] assignment in interceptor - Add unit tests for 401 refresh interceptor (5 tests covering retry, infinite loop prevention, onRefreshFailure, deduplication, config) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Collapse duplicate retry blocks in 401 interceptor into single path - Conditionally spread onUploadProgress (skip no-op callback on uploads without progress tracking, matching onDownloadProgress pattern) - Remove redundant setAuthenticated() from refresh callback (user is already authenticated when token refresh fires) - Hardcode health check query config in useHealthCheck hook (both callers used identical options) and flatten the API Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 3530b57e8692
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/src/lib/api-config.ts`:
- Around line 29-39: The current onRefreshFailure only calls clearAllUserStores
(a Zustand cleanup) and misses clearing React Query cache; move the full cleanup
into the React context logout implementation (useAuth().logout) so it calls
useQueryClient().invalidateQueries() (or clear/query cache) before calling
clearAllUserStores(), and update onRefreshFailure to invoke the context logout
when available (or fall back to the existing dynamic import +
window.location.reload() if logout cannot be reached) so both normal logout and
onRefreshFailure clear React Query state as well as Zustand stores.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 38c92482-7ce5-407a-adf3-8d93091fb19a
⛔ Files ignored due to path filters (4)
packages/api-client/package.jsonis excluded by!packages/api-client/**packages/api-client/src/__tests__/instance.test.tsis excluded by!packages/api-client/**packages/api-client/src/instance.tsis excluded by!packages/api-client/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!**/pnpm-lock.yaml
📒 Files selected for processing (6)
apps/web/src/components/layout/StatusIndicator.tsxapps/web/src/hooks/useHealthCheck.tsapps/web/src/lib/api-config.tsapps/web/src/lib/api/auth.tsapps/web/src/lib/api/ipfs.tsapps/web/src/routes/Login.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/src/components/layout/StatusIndicator.tsx
Summary
@cipherbox/api-clientpackage@cipherbox/api-client— fixes token refresh being broken on the web appapps/web/src/api/)lib/api/client.ts) and fetch-basedcustom-instance.tsChanges
@cipherbox/api-client(packages/api-client/src/instance.ts)refreshAccessToken,withCredentials,onRefreshFailuretoApiClientConfig/auth/refreshto prevent infinite loopsWeb app
apps/web/orval.config.ts, entireapps/web/src/api/directory,lib/api/client.tslib/api-config.ts(earlysetApiClientConfigwith refresh + cookies),hooks/useHealthCheck.ts(thin react-query wrapper)lib/api/auth.ts,lib/api/vault.ts,lib/api/ipfs.tsas thin adapters over@cipherbox/api-client../api/foo/footo@cipherbox/api-clientMotivation
The web app had three HTTP client paths: orval-generated fetch (
custom-instance.ts), hand-written axios (lib/api/client.ts), and bare axios/fetch (lib/api/ipfs.ts). Token refresh only existed on the axios client, which was only used during login. The orval-generated client (used for all normal operations) had zero 401 handling — so sessions died after 15min.Test plan
@cipherbox/api-clientbuilds clean/auth/refresh+ successful retry)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Refactor
Tests
Chores