fix(web-admin): fix broken dashboard stats cards (missing auth + double URL prefix)#6412
Conversation
…ion-count route The TYPESENSE_HOST env var already includes https:// prefix, causing the constructed URL to be https://https://... which fails silently. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three of four SWR fetchers (conversation-count, subscriptions, app-subscriptions) were using unauthenticated fetch, causing 401s from verifyAdmin(). Use the existing authenticatedFetcher with Bearer token, matching the pattern already used by the apps stats fetcher. Also add auth/token loading guards to prevent flash of error state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Greptile SummaryThis PR fixes three dashboard stat cards (Conversations, Subscriptions, App Subscriptions) that were returning 401 errors because their SWR fetchers lacked Bearer token auth headers, and additionally fixes a double URL scheme prefix bug in the Typesense route. The fix consistently applies the existing Confidence Score: 5/5Safe to merge — both fixes are minimal, targeted, and correct All changes are straightforward bug fixes with no logic errors, security issues, or regressions. The authenticated fetcher pattern applied to the 3 cards is identical to the already-working Apps card pattern. The URL scheme check in route.ts is defensive and handles both prefixed and bare hostnames correctly. No files require special attention
|
| Filename | Overview |
|---|---|
| web/admin/app/api/omi/stats/conversation-count/route.ts | Fixes double URL scheme prefix by checking if TYPESENSE_HOST already includes http(s):// before prepending |
| web/admin/components/dashboard/stats.tsx | Switches 3 SWR fetchers from unauthenticated fetch to authenticatedFetcher with token-keyed SWR keys, and adds authLoading/tokenLoading guards to all 3 loading states |
Sequence Diagram
sequenceDiagram
participant Browser
participant DashboardStats
participant Firebase
participant NextAPI
participant Typesense
Browser->>DashboardStats: Mount component
DashboardStats->>Firebase: user.getIdToken()
Firebase-->>DashboardStats: idToken
Note over DashboardStats: token set → SWR key becomes [url, token]
DashboardStats->>NextAPI: GET /stats/conversation-count<br/>Authorization: Bearer token
NextAPI->>NextAPI: verifyAdmin(request)
NextAPI->>Typesense: GET {TYPESENSE_HOST}/collections/conversations
Typesense-->>NextAPI: { num_documents: N }
NextAPI-->>DashboardStats: { totalConversations: N }
DashboardStats->>NextAPI: GET /stats/subscriptions<br/>Authorization: Bearer token
NextAPI-->>DashboardStats: { totalSubscriptions: N }
DashboardStats->>NextAPI: GET /stats/app-subscriptions<br/>Authorization: Bearer token
NextAPI-->>DashboardStats: { totalAppSubscriptions: N }
Reviews (1): Last reviewed commit: "fix(web-admin): add missing auth headers..." | Re-trigger Greptile
Extracts the duplicated Firebase token-fetching pattern into a reusable hook. Also provides authenticatedFetcher (for SWR) and authFetch (for direct fetch calls) so all hooks can send Bearer tokens consistently. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The notifications page used an unauthenticated fetcher for the /api/omi/stats/notifications endpoint which now requires verifyAdmin. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The user-notifications and regenerate endpoints now require verifyAdmin. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All distributor API routes (GET, POST, PUT) now require verifyAdmin. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All organization API routes (GET, POST, PATCH) now require verifyAdmin. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…fetch loops Replace authFetch() factory (unstable reference per render) with useAuthFetch() hook using useRef + useCallback for a stable identity. This prevents useEffect deps from triggering infinite refetch cycles in useDistributors and useOrganizations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch from unstable authFetch() factory to useAuthFetch() hook. Trigger fetch on token change only (not on callback identity change). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch from unstable authFetch() factory to useAuthFetch() hook. Trigger fetch on token change only (not on callback identity change). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove duplicated token-fetching boilerplate and local authenticatedFetcher — use the shared hook from useAuthToken.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add token check to loadNotifications and regenerateAll so they never fire unauthenticated requests. Simplify header construction now that token is guaranteed present. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename overallAvgSessionsPerUserPerDay to overallAvgPerUserPerDay to match the interface definition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CP9A — Level 1 Live Test: Changed-Path Coverage ChecklistComponent: Web admin (Next.js)
L1 SynthesisAll 8 changed paths (P1–P8) verified. Auth infrastructure (P1) proven via 401 responses on all 7 API endpoints tested without Bearer token. Stable fetch wrapper (P6, P7) prevents infinite re-render loops via by AI for @beastoin |
CP9B — Level 2 Live Test: Integrated Service + AppComponents: Web admin Next.js (server API routes = service, React client = app) Integrated Test Results
Page Load Verification (all return 200)
Infrastructure Verification
LimitationOmi backend ( L2 SynthesisAll 8 paths (P1–P8) verified at integration level. Client-server auth flow confirmed: unauthenticated requests rejected with 401, all 5 dashboard pages render correctly (200), JS bundles compile and load. The stable by AI for @beastoin |
|
lgtm |
Documents the single-fetch-layer rule: all client-side API fetches must use useAuthToken/authenticatedFetcher (SWR) or useAuthFetch (mutations). Lists banned patterns to prevent PR #6412 class of bugs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents the single-fetch-layer rule: all client-side API fetches must use useAuthToken/authenticatedFetcher (SWR) or useAuthFetch (mutations). Lists banned patterns to prevent PR BasedHardware#6412 class of bugs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Fixes all broken web admin dashboard tabs (Dashboard, Notifications, Distributors, Organizations) by adding authentication headers to client-side API calls.
Root cause: When
verifyAdmin()was added to server-side API routes, the client-side fetchers were never updated to include Firebase ID tokens in Authorization headers — all API calls returned 401.Changes:
hooks/useAuthToken.tswithuseAuthToken(),authenticatedFetcher, anduseAuthFetch()hooksauthFetch(token)factory with stableuseAuthFetch()hook (prevents infinite re-render loops)useAuthFetch()migrationhttps://https://...)overallAvgSessionsPerUserPerDay→overallAvgPerUserPerDay)Files Changed (8)
hooks/useAuthToken.tscomponents/dashboard/stats.tsxapi/omi/stats/conversation-count/route.tsdashboard/notifications/page.tsxdashboard/notifications/prompt-tester.tsxhooks/useDistributors.tsuseAuthFetchhookhooks/useOrganizations.tsuseAuthFetchhookdashboard/analytics/page.tsxTest Plan
next build— 0 errors)tsc --noEmit— 0 errors)Risks
NEXT_PUBLIC_DEV_BYPASS_AUTH=1) sets a mock user withoutgetIdToken()— token will be null in bypass mode, so API calls won't fire. This is pre-existing behavior unrelated to this PR.Closes #6375
🤖 Generated with Claude Code