mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-14 00:07:01 +08:00
fix(web): add auth guards to prevent unauthorized API calls (#934)
Add `user && token` guard to all authenticated SWR calls to prevent
requests with `Authorization: Bearer null` when users refresh the page
before AuthContext finishes loading the token from localStorage.
## Problem
When users refresh the page:
1. React components mount immediately
2. SWR hooks fire API requests
3. AuthContext is still loading token from localStorage
4. Requests sent with `Authorization: Bearer null`
5. Backend returns 401 errors
This causes:
- Unnecessary 401 errors in backend logs
- Error messages in browser console
- Poor user experience on page refresh
## Solution
Add auth check to SWR key conditions using pattern:
```typescript
user && token && condition ? key : null
```
When `user` or `token` is null, SWR key becomes `null`, preventing the request.
Once AuthContext loads, SWR automatically revalidates and fetches data.
## Changes
**TraderDashboard.tsx** (5 auth guards added):
- status: `user && token && selectedTraderId ? 'status-...' : null`
- account: `user && token && selectedTraderId ? 'account-...' : null`
- positions: `user && token && selectedTraderId ? 'positions-...' : null`
- decisions: `user && token && selectedTraderId ? 'decisions/...' : null`
- stats: `user && token && selectedTraderId ? 'statistics-...' : null`
**EquityChart.tsx** (2 auth guards added + useAuth import):
- Import `useAuth` from '../contexts/AuthContext'
- Add `const { user, token } = useAuth()`
- history: `user && token && traderId ? 'equity-history-...' : null`
- account: `user && token && traderId ? 'account-...' : null`
**apiGuard.test.ts** (new file, 370 lines):
- Comprehensive unit tests covering all auth guard scenarios
- Tests for null user, null token, valid auth states
- Tests for all 7 SWR calls (5 in TraderDashboard + 2 in EquityChart)
## Testing
- ✅ TypeScript compilation passed
- ✅ Vite build passed (2.81s)
- ✅ All modifications are additive (no logic changes)
- ✅ SWR auto-revalidation ensures data loads after auth completes
## Benefits
1. **No more 401 errors on refresh**: Auth guards prevent premature requests
2. **Cleaner logs**: Backend no longer receives invalid Bearer null requests
3. **Better UX**: No error flashes in console on page load
4. **Consistent pattern**: All authenticated endpoints use same guard logic
## Context
This PR supersedes closed PR #881, which had conflicts due to PR #872
(frontend refactor with React Router). This implementation is based on
the latest upstream/dev with the new architecture.
Related: PR #881 (closed), PR #872 (Frontend Refactor)
Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com>
Co-authored-by: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
committed by
GitHub
parent
21cc6e0bcd
commit
5fec086434
@@ -93,7 +93,7 @@ export default function TraderDashboard() {
|
||||
|
||||
// 如果在trader页面,获取该trader的数据
|
||||
const { data: status } = useSWR<SystemStatus>(
|
||||
selectedTraderId ? `status-${selectedTraderId}` : null,
|
||||
user && token && selectedTraderId ? `status-${selectedTraderId}` : null,
|
||||
() => api.getStatus(selectedTraderId),
|
||||
{
|
||||
refreshInterval: 15000,
|
||||
@@ -103,7 +103,7 @@ export default function TraderDashboard() {
|
||||
)
|
||||
|
||||
const { data: account } = useSWR<AccountInfo>(
|
||||
selectedTraderId ? `account-${selectedTraderId}` : null,
|
||||
user && token && selectedTraderId ? `account-${selectedTraderId}` : null,
|
||||
() => api.getAccount(selectedTraderId),
|
||||
{
|
||||
refreshInterval: 15000,
|
||||
@@ -113,7 +113,7 @@ export default function TraderDashboard() {
|
||||
)
|
||||
|
||||
const { data: positions } = useSWR<Position[]>(
|
||||
selectedTraderId ? `positions-${selectedTraderId}` : null,
|
||||
user && token && selectedTraderId ? `positions-${selectedTraderId}` : null,
|
||||
() => api.getPositions(selectedTraderId),
|
||||
{
|
||||
refreshInterval: 15000,
|
||||
@@ -123,7 +123,7 @@ export default function TraderDashboard() {
|
||||
)
|
||||
|
||||
const { data: decisions } = useSWR<DecisionRecord[]>(
|
||||
selectedTraderId
|
||||
user && token && selectedTraderId
|
||||
? `decisions/latest-${selectedTraderId}-${decisionLimit}`
|
||||
: null,
|
||||
() => api.getLatestDecisions(selectedTraderId, decisionLimit),
|
||||
@@ -135,7 +135,7 @@ export default function TraderDashboard() {
|
||||
)
|
||||
|
||||
const { data: stats } = useSWR<Statistics>(
|
||||
selectedTraderId ? `statistics-${selectedTraderId}` : null,
|
||||
user && token && selectedTraderId ? `statistics-${selectedTraderId}` : null,
|
||||
() => api.getStatistics(selectedTraderId),
|
||||
{
|
||||
refreshInterval: 30000,
|
||||
|
||||
Reference in New Issue
Block a user