90 lines
2.5 KiB
TypeScript
90 lines
2.5 KiB
TypeScript
import type { ApiError } from '~/types'
|
|
|
|
export const useApi = () => {
|
|
const config = useRuntimeConfig()
|
|
const authStore = useAuthStore()
|
|
|
|
const baseURL = config.public.apiBase as string
|
|
|
|
const request = async <T>(
|
|
endpoint: string,
|
|
options: RequestInit = {}
|
|
): Promise<T> => {
|
|
const headers: HeadersInit = {
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
}
|
|
|
|
if (authStore.token) {
|
|
headers['Authorization'] = `Bearer ${authStore.token}`
|
|
}
|
|
|
|
const response = await fetch(`${baseURL}${endpoint}`, {
|
|
...options,
|
|
headers,
|
|
credentials: 'include',
|
|
})
|
|
|
|
if (response.status === 401) {
|
|
try {
|
|
const refreshed = await authStore.refreshToken()
|
|
if (refreshed) {
|
|
headers['Authorization'] = `Bearer ${authStore.token}`
|
|
const retryResponse = await fetch(`${baseURL}${endpoint}`, {
|
|
...options,
|
|
headers,
|
|
credentials: 'include',
|
|
})
|
|
if (!retryResponse.ok) {
|
|
const retryError = await retryResponse.json().catch(() => ({}))
|
|
throw {
|
|
message: retryError.message || 'Request failed',
|
|
status: retryResponse.status,
|
|
} as ApiError
|
|
}
|
|
return retryResponse.json()
|
|
}
|
|
} catch {
|
|
authStore.logout()
|
|
navigateTo('/auth/login')
|
|
throw { message: 'Session expired', status: 401 } as ApiError
|
|
}
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const error: ApiError = await response.json().catch(() => ({
|
|
message: 'Network error',
|
|
}))
|
|
error.status = response.status
|
|
throw error
|
|
}
|
|
|
|
if (response.status === 204) {
|
|
return {} as T
|
|
}
|
|
|
|
return response.json()
|
|
}
|
|
|
|
return {
|
|
get: <T>(endpoint: string) => request<T>(endpoint),
|
|
post: <T>(endpoint: string, body?: unknown) =>
|
|
request<T>(endpoint, { method: 'POST', body: body ? JSON.stringify(body) : undefined }),
|
|
put: <T>(endpoint: string, body?: unknown) =>
|
|
request<T>(endpoint, { method: 'PUT', body: body ? JSON.stringify(body) : undefined }),
|
|
delete: <T>(endpoint: string) =>
|
|
request<T>(endpoint, { method: 'DELETE' }),
|
|
upload: <T>(endpoint: string, formData: FormData) => {
|
|
const headers: HeadersInit = {}
|
|
if (authStore.token) {
|
|
headers['Authorization'] = `Bearer ${authStore.token}`
|
|
}
|
|
return request<T>(endpoint, {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers,
|
|
})
|
|
},
|
|
}
|
|
}
|