File size: 4,127 Bytes
37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 37b5223 42a0d15 | 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | import axios, { AxiosError } from 'axios'
import type {
QueryResponse,
Report,
SearchFilters,
FeedbackPayload,
GraphData,
StreamEvent,
} from '@/types'
import { useAuthStore } from '@/stores/auth'
const API_BASE = import.meta.env.VITE_API_URL || '/api'
const client = axios.create({
baseURL: API_BASE,
timeout: 60000,
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
})
// Inject the JWT on every request
client.interceptors.request.use((config) => {
const auth = useAuthStore()
if (auth.accessToken) {
config.headers.Authorization = `Bearer ${auth.accessToken}`
}
return config
})
// Track rate-limit headers + show the auth modal on 429
client.interceptors.response.use(
(response) => {
const auth = useAuthStore()
auth.updateRateFromHeaders(response.headers as unknown as Headers)
return response
},
(error: AxiosError) => {
const auth = useAuthStore()
// Feed any rate headers we DID get back into the store
if (error.response?.headers) {
try {
auth.updateRateFromHeaders(error.response.headers as unknown as Headers)
} catch {
// axios headers are not always Headers — that's fine
}
}
if (error.response?.status === 429) {
// Open the signup modal so the user can convert
auth.openAuthModal()
throw new Error('Daily limit reached. Sign up for 25× more queries.')
}
if (error.response?.status === 401) {
// Token rejected — clear it and force re-auth
auth.$reset?.()
throw new Error('Your session has expired. Please sign in again.')
}
throw error
}
)
export const api = {
// Health
health: () => client.get('/health'),
// RAG query (non-streaming)
query: async (
query: string,
filters?: SearchFilters,
language: string = 'en',
mode: string = 'concise'
) => {
const response = await client.post<QueryResponse>('/query', {
query,
language,
mode,
filters,
})
return response.data
},
// Streaming query (SSE)
queryStream: (
query: string,
onEvent: (event: StreamEvent) => void,
filters?: SearchFilters
): EventSource => {
const auth = useAuthStore()
const url = new URL(`${API_BASE}/agent/stream`, window.location.origin)
// EventSource doesn't support custom headers, so we pass the JWT
// as a query param fallback (the server may ignore if it prefers headers).
if (auth.accessToken) {
url.searchParams.set('access_token', auth.accessToken)
}
const eventSource = new EventSource(url.toString())
eventSource.onmessage = (event) => {
try {
const data = JSON.parse(event.data)
onEvent(data)
} catch {
onEvent({ event: 'error', data: { message: 'Bad event payload' } })
}
}
eventSource.onerror = () => {
onEvent({ event: 'error', data: { message: 'Stream connection failed' } })
eventSource.close()
}
return eventSource
},
// Documents
getDocuments: async (params?: {
auditee?: string
fy?: string
limit?: number
offset?: number
}) => {
const response = await client.get<Report[]>('/documents', { params })
return response.data
},
getReport: async (id: string) => {
const response = await client.get<Report>(`/documents/${id}`)
return response.data
},
// Graph
graphQuery: async (cypher: string) => {
const response = await client.post<GraphData>('/graph/query', {
cypher,
read_only: true,
})
return response.data
},
getEntityGraph: async (entityId: string) => {
const response = await client.get<GraphData>(`/graph/entity/${entityId}`)
return response.data
},
// Feedback
submitFeedback: async (payload: FeedbackPayload) => {
const response = await client.post('/feedback', payload)
return response.data
},
// Suggestions
getSuggestions: async (query: string) => {
const response = await client.get<string[]>('/suggestions', {
params: { q: query },
})
return response.data
},
}
export default api
|