Auto-redirect to login on 401 API responses
When a session token expires, API calls return 401 but nothing caught it — errors were shown as generic dialogs or swallowed. Now both apiClient and streamingService detect 401 responses and clear auth state, which causes App.tsx to render the login modal automatically.
This commit is contained in:
parent
3acf8db7af
commit
a1829957c1
4 changed files with 29 additions and 1 deletions
|
|
@ -18,6 +18,8 @@ import { Button } from './components/ui/button';
|
||||||
import { Filter } from 'lucide-react';
|
import { Filter } from 'lucide-react';
|
||||||
import type { GeoJSONFeatureCollection, PropertyProperties, PropertyFeature, POI, POITravelFilter } from '@/types';
|
import type { GeoJSONFeatureCollection, PropertyProperties, PropertyFeature, POI, POITravelFilter } from '@/types';
|
||||||
import { refreshListings, streamListingGeoJSON, fetchUserPOIs, type StreamingProgress } from '@/services';
|
import { refreshListings, streamListingGeoJSON, fetchUserPOIs, type StreamingProgress } from '@/services';
|
||||||
|
import { setOnUnauthorized } from '@/services/apiClient';
|
||||||
|
import { clearPasskeyUser } from './auth/passkeyService';
|
||||||
import { poiMetricPropertyName, injectPoiMetricProperty } from '@/utils/poiUtils';
|
import { poiMetricPropertyName, injectPoiMetricProperty } from '@/utils/poiUtils';
|
||||||
import { useTaskProgress } from '@/hooks/useTaskProgress';
|
import { useTaskProgress } from '@/hooks/useTaskProgress';
|
||||||
|
|
||||||
|
|
@ -94,6 +96,13 @@ function App() {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setOnUnauthorized(() => {
|
||||||
|
clearPasskeyUser();
|
||||||
|
setUser(null);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handlePasskeyLogin = (passkeyUser: AuthUser) => {
|
const handlePasskeyLogin = (passkeyUser: AuthUser) => {
|
||||||
setUser(passkeyUser);
|
setUser(passkeyUser);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,18 @@
|
||||||
import type { AuthUser } from '@/auth/types';
|
import type { AuthUser } from '@/auth/types';
|
||||||
import { ApiError } from '@/types';
|
import { ApiError } from '@/types';
|
||||||
|
|
||||||
|
let onUnauthorized: (() => void) | null = null;
|
||||||
|
|
||||||
|
export function setOnUnauthorized(handler: () => void): void {
|
||||||
|
onUnauthorized = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fireUnauthorized(): void {
|
||||||
|
if (onUnauthorized) {
|
||||||
|
onUnauthorized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface RequestOptions {
|
export interface RequestOptions {
|
||||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||||
params?: Record<string, string | number | boolean | Date | undefined>;
|
params?: Record<string, string | number | boolean | Date | undefined>;
|
||||||
|
|
@ -61,6 +73,9 @@ export async function apiRequest<T>(
|
||||||
const response = await fetch(url, fetchOptions);
|
const response = await fetch(url, fetchOptions);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
if (response.status === 401) {
|
||||||
|
fireUnauthorized();
|
||||||
|
}
|
||||||
throw new ApiError(`Error: ${response.status}`, response.status);
|
throw new ApiError(`Error: ${response.status}`, response.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import type { PropertyFeature } from '@/types';
|
||||||
import type { ParameterValues } from '@/components/FilterPanel';
|
import type { ParameterValues } from '@/components/FilterPanel';
|
||||||
import { ApiError } from '@/types';
|
import { ApiError } from '@/types';
|
||||||
import { API_ENDPOINTS } from '@/constants';
|
import { API_ENDPOINTS } from '@/constants';
|
||||||
|
import { fireUnauthorized } from './apiClient';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build query string from parameters object
|
* Build query string from parameters object
|
||||||
|
|
@ -87,6 +88,9 @@ export async function* streamListingGeoJSON(
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
if (response.status === 401) {
|
||||||
|
fireUnauthorized();
|
||||||
|
}
|
||||||
throw new ApiError(`Error: ${response.status}`, response.status);
|
throw new ApiError(`Error: ${response.status}`, response.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"root":["./src/App.tsx","./src/AppSidebar.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/auth/authService.ts","./src/auth/config.ts","./src/auth/errors.ts","./src/auth/passkeyService.ts","./src/auth/types.ts","./src/components/ActiveQuery.tsx","./src/components/AlertError.tsx","./src/components/AuthCallback.tsx","./src/components/FilterPanel.tsx","./src/components/Header.tsx","./src/components/HealthIndicator.tsx","./src/components/ListView.tsx","./src/components/LoginModal.tsx","./src/components/Map.tsx","./src/components/POIManager.tsx","./src/components/PropertyCard.tsx","./src/components/Spinner.tsx","./src/components/StatsBar.tsx","./src/components/StreamingProgressBar.tsx","./src/components/TaskIndicator.tsx","./src/components/TaskProgressDrawer.tsx","./src/components/VisualizationCard.tsx","./src/components/ui/DatePicker.tsx","./src/components/ui/accordion.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/breadcrumb.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/checkbox.tsx","./src/components/ui/dialog.tsx","./src/components/ui/form.tsx","./src/components/ui/hover-card.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/range-slider-field.tsx","./src/components/ui/scroll-area.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/slider.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tooltip.tsx","./src/constants/colorSchemes.ts","./src/constants/index.ts","./src/hooks/use-mobile.ts","./src/lib/utils.ts","./src/services/apiClient.ts","./src/services/healthService.ts","./src/services/index.ts","./src/services/listingService.ts","./src/services/poiService.ts","./src/services/streamingService.ts","./src/services/taskService.ts","./src/types/index.ts","./src/utils/mapUtils.ts","./src/utils/poiUtils.ts"],"version":"5.8.3"}
|
{"root":["./src/app.tsx","./src/appsidebar.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/auth/authservice.ts","./src/auth/config.ts","./src/auth/errors.ts","./src/auth/passkeyservice.ts","./src/auth/types.ts","./src/components/activequery.tsx","./src/components/alerterror.tsx","./src/components/authcallback.tsx","./src/components/filterpanel.tsx","./src/components/header.tsx","./src/components/healthindicator.tsx","./src/components/listview.tsx","./src/components/loginmodal.tsx","./src/components/map.tsx","./src/components/poimanager.tsx","./src/components/propertycard.tsx","./src/components/spinner.tsx","./src/components/statsbar.tsx","./src/components/streamingprogressbar.tsx","./src/components/taskindicator.tsx","./src/components/taskprogressdrawer.tsx","./src/components/visualizationcard.tsx","./src/components/ui/datepicker.tsx","./src/components/ui/accordion.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/breadcrumb.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/checkbox.tsx","./src/components/ui/dialog.tsx","./src/components/ui/form.tsx","./src/components/ui/hover-card.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/range-slider-field.tsx","./src/components/ui/scroll-area.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/slider.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tooltip.tsx","./src/constants/colorschemes.ts","./src/constants/index.ts","./src/hooks/use-mobile.ts","./src/hooks/usetaskprogress.ts","./src/lib/utils.ts","./src/services/apiclient.ts","./src/services/healthservice.ts","./src/services/index.ts","./src/services/listingservice.ts","./src/services/poiservice.ts","./src/services/streamingservice.ts","./src/services/taskservice.ts","./src/types/index.ts","./src/utils/maputils.ts","./src/utils/poiutils.ts"],"version":"5.8.3"}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue