Download frontend/src/services/api.ts from Punit1/Enterprise-AI-Copilot: direct link, hf CLI and curl.
- Browser
- Download file 8.59 kB
-
https://huggingface.co/spaces/Punit1/Enterprise-AI-Copilot/resolve/main/frontend/src/services/api.ts
- Command line
-
hf download hf://spaces/Punit1/Enterprise-AI-Copilot/frontend/src/services/api.ts
-
curl -L -o api.ts https://huggingface.co/spaces/Punit1/Enterprise-AI-Copilot/resolve/main/frontend/src/services/api.ts
8.59 kB
| import axios, { AxiosInstance, AxiosError } from 'axios'; | |
| import type { RegisterResponse, TokenResponse, User } from '@/types'; | |
| const BASE_URL = import.meta.env.VITE_API_URL | |
| ? `${import.meta.env.VITE_API_URL}/api/v1` | |
| : '/api/v1'; | |
| // ββ Axios Instance βββββββββββββββββββββββββββββββββββββββββββ | |
| const api: AxiosInstance = axios.create({ | |
| baseURL: BASE_URL, | |
| headers: { 'Content-Type': 'application/json' }, | |
| timeout: 60000, | |
| }); | |
| // ββ Token Storage ββββββββββββββββββββββββββββββββββββββββββββ | |
| const TOKEN_KEY = 'copilot_access_token'; | |
| const REFRESH_KEY = 'copilot_refresh_token'; | |
| export const tokenStorage = { | |
| getAccess: () => localStorage.getItem(TOKEN_KEY), | |
| getRefresh: () => localStorage.getItem(REFRESH_KEY), | |
| setTokens: (access: string, refresh: string) => { | |
| localStorage.setItem(TOKEN_KEY, access); | |
| localStorage.setItem(REFRESH_KEY, refresh); | |
| }, | |
| clear: () => { | |
| localStorage.removeItem(TOKEN_KEY); | |
| localStorage.removeItem(REFRESH_KEY); | |
| }, | |
| }; | |
| // ββ Request Interceptor: attach token ββββββββββββββββββββββββ | |
| api.interceptors.request.use((config) => { | |
| const token = tokenStorage.getAccess(); | |
| if (token) { | |
| config.headers.Authorization = `Bearer ${token}`; | |
| } | |
| return config; | |
| }); | |
| // ββ Response Interceptor: auto-refresh on 401 ββββββββββββββββ | |
| let isRefreshing = false; | |
| let failedQueue: Array<{ resolve: (token: string) => void; reject: (err: unknown) => void }> = []; | |
| const processQueue = (error: unknown, token: string | null = null) => { | |
| failedQueue.forEach((p) => (token ? p.resolve(token) : p.reject(error))); | |
| failedQueue = []; | |
| }; | |
| api.interceptors.response.use( | |
| (response) => response, | |
| async (error: AxiosError) => { | |
| const originalRequest = error.config as typeof error.config & { _retry?: boolean }; | |
| if (error.response?.status === 401 && !originalRequest._retry) { | |
| if (isRefreshing) { | |
| return new Promise((resolve, reject) => { | |
| failedQueue.push({ resolve, reject }); | |
| }) | |
| .then((token) => { | |
| originalRequest.headers!['Authorization'] = `Bearer ${token}`; | |
| return api(originalRequest); | |
| }) | |
| .catch((err) => Promise.reject(err)); | |
| } | |
| originalRequest._retry = true; | |
| isRefreshing = true; | |
| const refreshToken = tokenStorage.getRefresh(); | |
| if (!refreshToken) { | |
| tokenStorage.clear(); | |
| window.location.href = '/login'; | |
| return Promise.reject(error); | |
| } | |
| try { | |
| const { data } = await axios.post<TokenResponse>(`${BASE_URL}/auth/refresh`, { | |
| refresh_token: refreshToken, | |
| }); | |
| tokenStorage.setTokens(data.access_token, data.refresh_token); | |
| processQueue(null, data.access_token); | |
| originalRequest.headers!['Authorization'] = `Bearer ${data.access_token}`; | |
| return api(originalRequest); | |
| } catch (refreshError) { | |
| processQueue(refreshError, null); | |
| tokenStorage.clear(); | |
| window.location.href = '/login'; | |
| return Promise.reject(refreshError); | |
| } finally { | |
| isRefreshing = false; | |
| } | |
| } | |
| return Promise.reject(error); | |
| } | |
| ); | |
| // ββ Auth API ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const authApi = { | |
| register: (data: { email: string; password: string; full_name: string; tenant_name: string }) => | |
| api.post<RegisterResponse>('/auth/register', data).then((r) => r.data), | |
| login: (email: string, password: string) => { | |
| const form = new URLSearchParams(); | |
| form.append('username', email); | |
| form.append('password', password); | |
| return api | |
| .post<TokenResponse>('/auth/login', form, { | |
| headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
| }) | |
| .then((r) => r.data); | |
| }, | |
| logout: () => api.post('/auth/logout').then((r) => r.data), | |
| me: () => api.get<User>('/auth/me').then((r) => r.data), | |
| refresh: (refresh_token: string) => | |
| api.post<TokenResponse>('/auth/refresh', { refresh_token }).then((r) => r.data), | |
| }; | |
| // ββ Chat API ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const chatApi = { | |
| sendMessage: (message: string, session_id?: string) => | |
| api | |
| .post('/chat/', { message, session_id }) | |
| .then((r) => r.data), | |
| streamMessage: async ( | |
| message: string, | |
| session_id: string | undefined, | |
| onToken: (token: string) => void, | |
| onMeta: (meta: { session_id: string; agent_type: string; routing_decision: string }) => void, | |
| onSources: (sources: any) => void, | |
| onDone: (info: { tokens_used: number; latency_ms: number }) => void, | |
| onError: (error: string) => void, | |
| ) => { | |
| const token = tokenStorage.getAccess(); | |
| try { | |
| const res = await fetch(`${BASE_URL}/chat/stream`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| Authorization: `Bearer ${token}`, | |
| }, | |
| body: JSON.stringify({ message, session_id }), | |
| }); | |
| if (!res.ok) { | |
| onError(`HTTP ${res.status}`); | |
| return; | |
| } | |
| const reader = res.body?.getReader(); | |
| if (!reader) { onError('No response stream'); return; } | |
| const decoder = new TextDecoder(); | |
| let buffer = ''; | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| buffer += decoder.decode(value, { stream: true }); | |
| const lines = buffer.split('\n'); | |
| buffer = lines.pop() || ''; | |
| for (const line of lines) { | |
| if (!line.startsWith('data: ')) continue; | |
| try { | |
| const data = JSON.parse(line.slice(6)); | |
| switch (data.type) { | |
| case 'meta': onMeta(data); break; | |
| case 'token': onToken(data.content); break; | |
| case 'sources': onSources(data); break; | |
| case 'done': onDone(data); break; | |
| } | |
| } catch { | |
| // skip malformed JSON | |
| } | |
| } | |
| } | |
| } catch (err: any) { | |
| onError(err.message || 'Stream connection failed'); | |
| } | |
| }, | |
| getSessions: () => api.get('/chat/sessions').then((r) => r.data), | |
| getSessionMessages: (sessionId: string) => | |
| api.get(`/chat/sessions/${sessionId}`).then((r) => r.data), | |
| deleteSession: (sessionId: string) => | |
| api.delete(`/chat/sessions/${sessionId}`).then((r) => r.data), | |
| }; | |
| // ββ Documents API βββββββββββββββββββββββββββββββββββββββββββββ | |
| export const documentsApi = { | |
| upload: (file: File, docType: string = 'general') => { | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| formData.append('doc_type', docType); | |
| return api | |
| .post('/documents/upload', formData, { | |
| headers: { 'Content-Type': 'multipart/form-data' }, | |
| }) | |
| .then((r) => r.data); | |
| }, | |
| list: (docType?: string) => { | |
| const params: Record<string, string> = {}; | |
| if (docType) params['doc_type'] = docType; | |
| return api.get('/documents/', { params }).then((r) => r.data); | |
| }, | |
| get: (id: string) => api.get(`/documents/${id}`).then((r) => r.data), | |
| getStatus: (id: string) => api.get(`/documents/${id}/status`).then((r) => r.data), | |
| delete: (id: string) => api.delete(`/documents/${id}`).then((r) => r.data), | |
| }; | |
| // ββ Admin API βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const adminApi = { | |
| getUsers: () => api.get('/admin/users').then((r) => r.data), | |
| inviteUser: (data: { email: string; full_name: string; role: string; password?: string }) => | |
| api.post('/admin/users/invite', data).then((r) => r.data), | |
| getAuditLogs: () => api.get('/admin/audit-logs').then((r) => r.data), | |
| getTenant: () => api.get('/admin/tenant').then((r) => r.data), | |
| }; | |
| // ββ Health API ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const healthApi = { | |
| check: () => api.get('/health', { baseURL: '' }).then((r) => r.data), | |
| }; | |
| export default api; | |