Spaces:
Sleeping
Sleeping
File size: 3,131 Bytes
7949d20 fa3f9d1 7949d20 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | import { API_BASE } from '../utils/constants.js'
let _authToken = null
let _refreshToken = null
/**
* Set the current auth token to include in all API requests.
*/
export function setAuthToken(token) {
_authToken = token
}
/**
* Set a callback to refresh the token when a 401 is received.
* The callback should return the new token (or null if refresh fails).
*/
export function setTokenRefresh(fn) {
_refreshToken = fn
}
/**
* Get the auth headers object for direct fetch calls (e.g. chat SSE).
*/
export function getAuthHeaders() {
return _authToken ? { Authorization: `Bearer ${_authToken}` } : {}
}
/**
* Try to refresh the auth token via the registered callback.
* Returns true if the token was refreshed, false otherwise.
*/
export async function refreshAuthToken() {
if (!_refreshToken) return false
try {
const newToken = await _refreshToken()
if (newToken) {
_authToken = newToken
return true
}
} catch {
// Refresh failed
}
return false
}
function authHeaders() {
return getAuthHeaders()
}
async function request(url, options = {}) {
let retries = 0
const doFetch = async () => {
const res = await fetch(`${API_BASE}${url}`, {
headers: { 'Content-Type': 'application/json', ...authHeaders(), ...options.headers },
...options,
})
// On 401, try refreshing the token once and retry
if (res.status === 401 && retries < 1) {
retries++
const refreshed = await refreshAuthToken()
if (refreshed) {
return doFetch()
}
}
if (!res.ok) {
throw new Error(`API error: ${res.status} ${res.statusText}`)
}
return res.json()
}
return doFetch()
}
export async function get(url) {
return request(url, { method: 'GET' })
}
export async function post(url, body) {
return request(url, { method: 'POST', body: JSON.stringify(body) })
}
export async function patch(url, body) {
return request(url, { method: 'PATCH', body: JSON.stringify(body) })
}
export async function del(url) {
return request(url, { method: 'DELETE' })
}
/**
* Upload a file (multipart/form-data) to the API with auth.
*/
export async function uploadFile(url, file, params = {}) {
const formData = new FormData()
formData.append('file', file)
const query = new URLSearchParams(params).toString()
const fullUrl = query ? `${url}?${query}` : url
let retries = 0
const doFetch = async () => {
const res = await fetch(`${API_BASE}${fullUrl}`, {
method: 'POST',
headers: authHeaders(),
body: formData,
})
// On 401, try refreshing the token once and retry
if (res.status === 401 && retries < 1) {
retries++
// FormData can only be sent once, need to recreate
const retryFormData = new FormData()
retryFormData.append('file', file)
const refreshed = await refreshAuthToken()
if (refreshed) {
return doFetch()
}
}
if (!res.ok) {
const body = await res.text().catch(() => '')
throw new Error(`Upload failed (${res.status}): ${body}`)
}
return res.json()
}
return doFetch()
}
|