Skip to content

Commit 4863a83

Browse files
committed
fix: handle expired auth tokens by redirecting to login on 401
1 parent 7512820 commit 4863a83

File tree

11 files changed

+183
-290
lines changed

11 files changed

+183
-290
lines changed

src/app/businesses/[id]/page.tsx

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { useState, useEffect } from 'react';
44
import { useRouter, useParams } from 'next/navigation';
5+
import { authFetch } from '@/lib/auth/client';
56
import { Business, Wallet, TabType } from '@/components/business/types';
67
import { GeneralTab } from '@/components/business/GeneralTab';
78
import { WalletsTab } from '@/components/business/WalletsTab';
@@ -29,19 +30,10 @@ export default function BusinessDetailPage() {
2930

3031
const fetchBusiness = async () => {
3132
try {
32-
const token = localStorage.getItem('auth_token');
33-
if (!token) {
34-
router.push('/login');
35-
return;
36-
}
33+
const result = await authFetch(`/api/businesses/${businessId}`, {}, router);
34+
if (!result) return;
3735

38-
const response = await fetch(`/api/businesses/${businessId}`, {
39-
headers: {
40-
Authorization: `Bearer ${token}`,
41-
},
42-
});
43-
44-
const data = await response.json();
36+
const { response, data } = result;
4537

4638
if (!response.ok || !data.success) {
4739
setError(data.error || 'Failed to load business');
@@ -59,16 +51,10 @@ export default function BusinessDetailPage() {
5951

6052
const fetchWallets = async () => {
6153
try {
62-
const token = localStorage.getItem('auth_token');
63-
if (!token) return;
64-
65-
const response = await fetch(`/api/businesses/${businessId}/wallets`, {
66-
headers: {
67-
Authorization: `Bearer ${token}`,
68-
},
69-
});
54+
const result = await authFetch(`/api/businesses/${businessId}/wallets`, {}, router);
55+
if (!result) return;
7056

71-
const data = await response.json();
57+
const { response, data } = result;
7258

7359
if (response.ok && data.success) {
7460
setWallets(data.wallets);

src/app/businesses/page.tsx

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { useState, useEffect } from 'react';
44
import { useRouter } from 'next/navigation';
5+
import { authFetch } from '@/lib/auth/client';
56

67
interface Business {
78
id: string;
@@ -30,19 +31,10 @@ export default function BusinessesPage() {
3031

3132
const fetchBusinesses = async () => {
3233
try {
33-
const token = localStorage.getItem('auth_token');
34-
if (!token) {
35-
router.push('/login');
36-
return;
37-
}
38-
39-
const response = await fetch('/api/businesses', {
40-
headers: {
41-
Authorization: `Bearer ${token}`,
42-
},
43-
});
34+
const result = await authFetch('/api/businesses', {}, router);
35+
if (!result) return;
4436

45-
const data = await response.json();
37+
const { response, data } = result;
4638

4739
if (!response.ok || !data.success) {
4840
setError(data.error || 'Failed to load businesses');
@@ -82,27 +74,19 @@ export default function BusinessesPage() {
8274
setSaving(true);
8375

8476
try {
85-
const token = localStorage.getItem('auth_token');
86-
if (!token) {
87-
router.push('/login');
88-
return;
89-
}
90-
9177
const url = editingBusiness
9278
? `/api/businesses/${editingBusiness.id}`
9379
: '/api/businesses';
9480
const method = editingBusiness ? 'PATCH' : 'POST';
9581

96-
const response = await fetch(url, {
82+
const result = await authFetch(url, {
9783
method,
98-
headers: {
99-
'Content-Type': 'application/json',
100-
Authorization: `Bearer ${token}`,
101-
},
84+
headers: { 'Content-Type': 'application/json' },
10285
body: JSON.stringify(formData),
103-
});
86+
}, router);
87+
if (!result) return;
10488

105-
const data = await response.json();
89+
const { response, data } = result;
10690

10791
if (!response.ok || !data.success) {
10892
setError(data.error || 'Failed to save business');
@@ -125,20 +109,12 @@ export default function BusinessesPage() {
125109
}
126110

127111
try {
128-
const token = localStorage.getItem('auth_token');
129-
if (!token) {
130-
router.push('/login');
131-
return;
132-
}
133-
134-
const response = await fetch(`/api/businesses/${id}`, {
112+
const result = await authFetch(`/api/businesses/${id}`, {
135113
method: 'DELETE',
136-
headers: {
137-
Authorization: `Bearer ${token}`,
138-
},
139-
});
114+
}, router);
115+
if (!result) return;
140116

141-
const data = await response.json();
117+
const { response, data } = result;
142118

143119
if (!response.ok || !data.success) {
144120
setError(data.error || 'Failed to delete business');

src/app/dashboard/page.tsx

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
55
import Link from 'next/link';
66
import Papa from 'papaparse';
77
import { useRealtimePayments, type RealtimePayment } from '@/lib/realtime/useRealtimePayments';
8+
import { authFetch, requireAuth } from '@/lib/auth/client';
89

910
interface DashboardStats {
1011
total_payments: number;
@@ -126,25 +127,16 @@ export default function DashboardPage() {
126127

127128
const fetchDashboardData = async (businessId?: string) => {
128129
try {
129-
const token = localStorage.getItem('auth_token');
130-
if (!token) {
131-
router.push('/login');
132-
return;
133-
}
134-
135130
// Build URL with optional business_id filter
136131
let url = '/api/dashboard/stats';
137132
if (businessId) {
138133
url += `?business_id=${businessId}`;
139134
}
140135

141-
const response = await fetch(url, {
142-
headers: {
143-
Authorization: `Bearer ${token}`,
144-
},
145-
});
136+
const result = await authFetch(url, {}, router);
137+
if (!result) return; // Redirected to login
146138

147-
const data = await response.json();
139+
const { response, data } = result;
148140

149141
if (!response.ok || !data.success) {
150142
setError(data.error || 'Failed to load dashboard data');
@@ -221,11 +213,6 @@ export default function DashboardPage() {
221213
const exportToCSV = async () => {
222214
try {
223215
setExporting(true);
224-
const token = localStorage.getItem('auth_token');
225-
if (!token) {
226-
setError('Please log in to export payments');
227-
return;
228-
}
229216

230217
// Build URL with optional business_id filter
231218
let url = '/api/payments';
@@ -234,13 +221,10 @@ export default function DashboardPage() {
234221
}
235222

236223
// Fetch payments from the API (respects current business filter)
237-
const response = await fetch(url, {
238-
headers: {
239-
Authorization: `Bearer ${token}`,
240-
},
241-
});
224+
const fetchResult = await authFetch(url, {}, router);
225+
if (!fetchResult) return; // Redirected to login
242226

243-
const result = await response.json();
227+
const { response, data: result } = fetchResult;
244228

245229
if (!response.ok || !result.success) {
246230
setError(result.error || 'Failed to fetch payments for export');

src/app/payments/[id]/page.tsx

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { useState, useEffect, useCallback, useRef } from 'react';
44
import { useRouter, useParams } from 'next/navigation';
5+
import { authFetch } from '@/lib/auth/client';
56

67
const PAYMENT_EXPIRY_MINUTES = 15;
78
const POLL_INTERVAL_MS = 5000; // Poll every 5 seconds
@@ -117,18 +118,10 @@ export default function PaymentDetailPage() {
117118
setPaymentStatus(data.status);
118119

119120
// Fetch full payment details
120-
const token = localStorage.getItem('auth_token');
121-
if (token) {
122-
const paymentResponse = await fetch(`/api/payments/${paymentId}`, {
123-
headers: {
124-
Authorization: `Bearer ${token}`,
125-
},
126-
});
127-
if (paymentResponse.ok) {
128-
const paymentData = await paymentResponse.json();
129-
if (paymentData.success && paymentData.payment) {
130-
setPayment(paymentData.payment);
131-
}
121+
const paymentResult = await authFetch(`/api/payments/${paymentId}`, {});
122+
if (paymentResult && paymentResult.response.ok) {
123+
if (paymentResult.data.success && paymentResult.data.payment) {
124+
setPayment(paymentResult.data.payment);
132125
}
133126
}
134127

@@ -157,17 +150,12 @@ export default function PaymentDetailPage() {
157150
// Poll for payment status
158151
const pollPaymentStatus = useCallback(async () => {
159152
try {
160-
const token = localStorage.getItem('auth_token');
161-
if (!token) return;
153+
const result = await authFetch(`/api/payments/${paymentId}`, {});
154+
if (!result) return;
162155

163-
const response = await fetch(`/api/payments/${paymentId}`, {
164-
headers: {
165-
Authorization: `Bearer ${token}`,
166-
},
167-
});
156+
const { response, data } = result;
168157

169158
if (response.ok) {
170-
const data = await response.json();
171159
if (data.success && data.payment) {
172160
setPayment(data.payment);
173161
setPaymentStatus(data.payment.status);
@@ -210,19 +198,10 @@ export default function PaymentDetailPage() {
210198
useEffect(() => {
211199
const fetchPayment = async () => {
212200
try {
213-
const token = localStorage.getItem('auth_token');
214-
if (!token) {
215-
router.push('/login');
216-
return;
217-
}
201+
const result = await authFetch(`/api/payments/${paymentId}`, {}, router);
202+
if (!result) return;
218203

219-
const response = await fetch(`/api/payments/${paymentId}`, {
220-
headers: {
221-
Authorization: `Bearer ${token}`,
222-
},
223-
});
224-
225-
const data = await response.json();
204+
const { response, data } = result;
226205

227206
if (!response.ok || !data.success) {
228207
setError(data.error || 'Failed to load payment');

0 commit comments

Comments
 (0)