| |
| |
| |
|
|
| |
| |
| |
| |
| |
| const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || ''; |
|
|
| |
| |
| |
| |
| export function isBackendConfigured() { |
| return BACKEND_URL !== '' && BACKEND_URL !== undefined; |
| } |
|
|
| |
| |
| |
| |
| export function getBackendUrl() { |
| return BACKEND_URL; |
| } |
|
|
| |
| |
| |
| |
| |
| export function apiUrl(path) { |
| |
| const cleanPath = path.startsWith('/') ? path : `/${path}`; |
| return `${BACKEND_URL}${cleanPath}`; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export async function safeFetchJSON(url, options = {}) { |
| try { |
| const response = await fetch(url, options); |
| const contentType = response.headers.get('content-type'); |
|
|
| |
| if (!contentType || !contentType.includes('application/json')) { |
| |
| const text = await response.text(); |
|
|
| |
| if (text.trim().toLowerCase().startsWith('<!doctype') || |
| text.trim().toLowerCase().startsWith('<html')) { |
| throw new Error( |
| `Backend not reachable. Received HTML instead of JSON. ` + |
| `${!isBackendConfigured() ? 'VITE_BACKEND_URL environment variable is not configured. ' : ''}` + |
| `Please check your backend configuration.` |
| ); |
| } |
|
|
| |
| throw new Error(`Unexpected response type: ${contentType || 'unknown'}. Response: ${text.substring(0, 100)}`); |
| } |
|
|
| const data = await response.json(); |
|
|
| if (!response.ok) { |
| throw new Error(data.detail || data.error || data.message || `Request failed with status ${response.status}`); |
| } |
|
|
| return data; |
| } catch (error) { |
| |
| if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) { |
| throw new Error( |
| `Cannot connect to backend server. ` + |
| `${!isBackendConfigured() ? 'VITE_BACKEND_URL environment variable is not configured. ' : ''}` + |
| `Please check that the backend is running and accessible.` |
| ); |
| } |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| export function getAuthHeaders() { |
| const token = localStorage.getItem('github_token'); |
|
|
| if (!token) { |
| return {}; |
| } |
|
|
| return { |
| 'Authorization': `Bearer ${token}`, |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export async function authFetch(url, options = {}) { |
| const headers = { |
| ...getAuthHeaders(), |
| ...options.headers, |
| }; |
|
|
| return fetch(url, { |
| ...options, |
| headers, |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export async function authFetchJSON(url, options = {}) { |
| const headers = { |
| 'Content-Type': 'application/json', |
| ...getAuthHeaders(), |
| ...options.headers, |
| }; |
|
|
| const response = await fetch(url, { |
| ...options, |
| headers, |
| }); |
|
|
| if (!response.ok) { |
| const error = await response.json().catch(() => ({ detail: 'Request failed' })); |
| throw new Error(error.detail || error.message || 'Request failed'); |
| } |
|
|
| return response.json(); |
| } |