import { authService } from '~/services/auth.service'; let isRefreshing = false; let refreshPromise: Promise | null = null; /** * Get token or refresh if needed * Handles concurrent requests by sharing same refresh promise */ export const getValidToken = async (): Promise => { const tokenCookie = useCookie('token'); const refreshTokenCookie = useCookie('refreshToken'); // If we have token, return it if (tokenCookie.value) { return tokenCookie.value; } // No token but have refresh token, try to refresh if (refreshTokenCookie.value) { // If already refreshing, wait for that promise if (isRefreshing && refreshPromise) { return refreshPromise; } try { isRefreshing = true; const refreshToken = refreshTokenCookie.value; refreshPromise = authService.refreshToken(refreshToken).then(res => { // Update cookies useCookie('token').value = res.token; useCookie('refreshToken').value = res.refreshToken; return res.token; }); const newToken = await refreshPromise; return newToken; } catch (error) { console.error('Token refresh failed'); return null; } finally { isRefreshing = false; refreshPromise = null; } } return null; }; /** * Handle 401 error - try to refresh and return new token * Returns null if refresh fails */ export const handleUnauthorized = async (): Promise => { const refreshTokenCookie = useCookie('refreshToken'); if (!refreshTokenCookie.value) { return null; } // If already refreshing, wait for that promise if (isRefreshing && refreshPromise) { return refreshPromise; } try { isRefreshing = true; const refreshToken = refreshTokenCookie.value; refreshPromise = authService.refreshToken(refreshToken).then(res => { // Update cookies useCookie('token').value = res.token; useCookie('refreshToken').value = res.refreshToken; return res.token; }); const newToken = await refreshPromise; console.log('Token refreshed successfully'); return newToken; } catch (error) { console.error('Token refresh failed, need to login again'); // Clear all auth cookies useCookie('token').value = null; useCookie('refreshToken').value = null; useCookie('user').value = null; return null; } finally { isRefreshing = false; refreshPromise = null; } }; /** * Create authenticated fetch with auto token refresh */ export const createAuthFetch = () => { const config = useRuntimeConfig(); return async ( url: string, options: { method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; body?: any; headers?: Record; } = {} ): Promise => { const token = await getValidToken(); const makeRequest = async (authToken: string | null) => { return await $fetch(url, { baseURL: config.public.apiBaseUrl as string, method: options.method || 'GET', headers: { ...options.headers, ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}) }, body: options.body }); }; try { return await makeRequest(token); } catch (error: any) { // If 401, try to refresh if (error.response?.status === 401) { const newToken = await handleUnauthorized(); if (newToken) { // Retry with new token return await makeRequest(newToken); } // Redirect to login navigateTo('/login'); } throw error; } }; };