Aria / frontend /src /services /api.ts
teganmosi
Deploy Aria application with fixes for database, TTS, and WebSocket (LFS tracking applied)
f6d0a88
Raw
History Blame Contribute Delete
11.5 kB
// @ts-nocheck — full TS typing is a follow-up pass; Axios migration done here
import { axiosBase, axiosPrivate } from '../api/axios'
export const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8002/api/v1'
// Used only by chatStream (streaming generator — not compatible with Axios)
const getStreamHeaders = () => {
const token = localStorage.getItem('authToken')
return {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
}
}
// Auth Service
export const authService = {
login: async (email: string, password: string) => {
const { data } = await axiosBase.post('/auth/login', { email, password })
return data
},
register: async (email: string, password: string, fullName: string) => {
const { data } = await axiosBase.post('/auth/register', {
email,
password,
full_name: fullName,
})
return data
},
getMe: async () => {
const { data } = await axiosPrivate.get('/auth/me')
return data
},
logout: async () => {
const { data } = await axiosPrivate.post('/auth/logout')
return data
},
exchangeOAuthToken: async (accessToken: string) => {
const { data } = await axiosBase.post('/auth/oauth/exchange', {
access_token: accessToken,
})
return data
},
}
// Profile Service
export const profileService = {
getProfile: async () => {
const { data } = await axiosPrivate.get('/profile')
return data
},
updateProfile: async (profileData: unknown) => {
const { data } = await axiosPrivate.put('/profile', profileData)
return data
},
}
// Bible Service
export const bibleService = {
getChapter: async (book: string, chapter: number) => {
const { data } = await axiosBase.get(`/bible/chapter/${encodeURIComponent(book)}/${chapter}`)
return data
},
getVerse: async (book: string, chapter: number, verse: number) => {
const { data } = await axiosPrivate.get(
`/bible/verses/${encodeURIComponent(book)}/${chapter}/${verse}`
)
return data
},
searchVerses: async (query: string) => {
const { data } = await axiosPrivate.get(`/bible/search?query=${encodeURIComponent(query)}`)
return data
},
createStudySession: async (
book: string,
chapter: number,
verses: string | number[],
selectedText: string
) => {
let verseList: number[] = []
if (Array.isArray(verses)) {
verseList = verses.map((v) => parseInt(String(v))).filter((v) => !isNaN(v))
} else if (typeof verses === 'string' && verses.trim()) {
verseList = verses
.split('-')
.map((v) => parseInt(v.trim()))
.filter((v) => !isNaN(v))
}
const { data } = await axiosPrivate.post('/bible-study/sessions', {
book: book.trim(),
chapter: parseInt(String(chapter)),
verses: verseList,
selected_text: selectedText,
})
return data
},
getStudySessions: async () => {
const { data } = await axiosPrivate.get('/bible-study/sessions')
return data
},
getStudySession: async (sessionId: string) => {
const { data } = await axiosPrivate.get(`/bible-study/sessions/${sessionId}`)
return data
},
getStudySessionMessages: async (sessionId: string) => {
const { data } = await axiosPrivate.get(`/bible-study/sessions/${sessionId}/messages`)
return data
},
createStudyMessage: async (sessionId: string, role: string, content: string) => {
const { data } = await axiosPrivate.post(`/bible-study/sessions/${sessionId}/messages`, {
role,
content,
})
return data
},
}
// Emotional Support Service
export const emotionalSupportService = {
createSession: async (mood: string, situationDescription: string) => {
const { data } = await axiosPrivate.post('/emotional-support/sessions', {
mood,
situation_description: situationDescription,
})
return data
},
getSessions: async () => {
const { data } = await axiosPrivate.get('/emotional-support/sessions')
return data
},
getSessionMessages: async (sessionId: string) => {
const { data } = await axiosPrivate.get(
`/emotional-support/sessions/${sessionId}/messages`
)
return data
},
createMessage: async (sessionId: string, role: string, content: string) => {
const { data } = await axiosPrivate.post(
`/emotional-support/sessions/${sessionId}/messages`,
{ role, content }
)
return data
},
}
// Devotion Service
export const devotionService = {
getSettings: async () => {
const { data } = await axiosPrivate.get('/devotion/settings')
return data
},
saveSettings: async (preferredTime: string, durationMinutes: number | string) => {
const { data } = await axiosPrivate.put('/devotion/settings', {
preferred_time: preferredTime,
duration_minutes: parseInt(String(durationMinutes)),
})
return data
},
scheduleDevotion: async (scheduledFor: string, dayPlanSummary: string) => {
const { data } = await axiosPrivate.post('/devotion/schedule', {
scheduled_for: scheduledFor,
day_plan_summary: dayPlanSummary,
})
return data
},
getDevotions: async () => {
const { data } = await axiosPrivate.get('/devotion/devotions')
return data
},
completeDevotion: async (devotionId: string) => {
const { data } = await axiosPrivate.put(`/devotion/devotions/${devotionId}/complete`)
return data
},
createMessage: async (devotionId: string, role: string, content: string) => {
const { data } = await axiosPrivate.post(`/devotion/devotions/${devotionId}/messages`, {
role,
content,
})
return data
},
}
// AI Service
export const aiService = {
generate: async (messages: unknown, mode: string) => {
const { data } = await axiosPrivate.post('/ai/generate', { messages, mode })
return data
},
}
// Dedicated AI Chat Service with Persistence
export const aiChatService = {
getSessions: async () => {
const { data } = await axiosPrivate.get('/ai-chat/sessions')
return data
},
getMessages: async (sessionId: string) => {
const { data } = await axiosPrivate.get(`/ai-chat/sessions/${sessionId}/messages`)
return data
},
chat: async (messages: unknown, sessionId: string | null = null, mode = 'general') => {
const url = sessionId ? `/ai/chat?session_id=${sessionId}` : '/ai/chat'
const { data } = await axiosPrivate.post(url, { messages, mode })
return data
},
// Streaming generator — kept on native fetch; Axios does not support async generators
chatStream: async function* (
messages: unknown,
sessionId: string | null = null,
mode = 'general'
) {
const url = sessionId
? `${API_BASE_URL}/ai/chat/stream?session_id=${sessionId}`
: `${API_BASE_URL}/ai/chat/stream`
const response = await fetch(url, {
method: 'POST',
headers: getStreamHeaders(),
body: JSON.stringify({ messages, mode }),
})
if (!response.ok) {
if (response.status === 401) {
localStorage.removeItem('authToken')
if (
!window.location.pathname.includes('/login') &&
!window.location.pathname.includes('/register')
) {
window.location.href = '/login'
}
}
const error = await response.json().catch(() => ({ error: 'Unknown error' }))
throw new Error(error.error || `HTTP error! status: ${response.status}`)
}
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
while (true) {
const { done, value } = await reader.read()
if (done) break
yield decoder.decode(value, { stream: true })
}
},
}
// Voice Call Service — WebSocket-based real-time voice
export const voiceCallService = {
// C1 deferred: token in WebSocket URL — do not change
getWebSocketUrl: (callId: string) => {
let wsHost = import.meta.env.VITE_WS_URL || 'localhost:8002'
wsHost = wsHost.replace(/^(wss?):\/\//, '')
const isLocalhost = wsHost.startsWith('localhost') || wsHost.startsWith('127.0.0.1')
const wsProtocol = (!isLocalhost || window.location.protocol === 'https:') ? 'wss:' : 'ws:'
const token = localStorage.getItem('authToken')
return `${wsProtocol}//${wsHost}/ws/voice-call/${callId}?token=${token}`
},
createCallSession: async (mode = 'general') => {
const { data } = await axiosPrivate.post('/ai/voice-session', { mode })
return data
},
}
// Home Service
export const homeService = {
getHomeData: async () => {
const { data } = await axiosPrivate.get('/home/data')
return data
},
getVerse: async () => {
const { data } = await axiosPrivate.get('/home/verse')
return data
},
getActivity: async (limit = 5) => {
const { data } = await axiosPrivate.get(`/home/activity?limit=${limit}`)
return data
},
getStats: async () => {
const { data } = await axiosPrivate.get('/home/stats')
return data
},
}
// Dashboard Service — Combined data for faster initial load
export const dashboardService = {
getDashboardData: async () => {
try {
const { data } = await axiosPrivate.get('/dashboard')
return data
} catch (err) {
if (axios.isAxiosError(err) && err.response?.status === 404) {
return null
}
throw err
}
},
}
// Prayer Service
export const prayerService = {
createPrayer: async (prayerData: unknown) => {
const { data } = await axiosPrivate.post('/prayers', prayerData)
return data
},
getPrayers: async () => {
const { data } = await axiosPrivate.get('/prayers')
return data
},
deletePrayer: async (prayerId: string) => {
const { data } = await axiosPrivate.delete(`/prayers/${prayerId}`)
return data
},
}
// Notes Service
export const notesService = {
createNote: async (noteData: unknown) => {
const { data } = await axiosPrivate.post('/notes', noteData)
return data
},
getNotes: async (sourceType: string | null = null) => {
const url = sourceType
? `/notes?source_type=${encodeURIComponent(sourceType)}`
: '/notes'
const { data } = await axiosPrivate.get(url)
return data
},
getNote: async (noteId: string) => {
const { data } = await axiosPrivate.get(`/notes/${noteId}`)
return data
},
updateNote: async (noteId: string, noteData: unknown) => {
const { data } = await axiosPrivate.put(`/notes/${noteId}`, noteData)
return data
},
deleteNote: async (noteId: string) => {
const { data } = await axiosPrivate.delete(`/notes/${noteId}`)
return data
},
unlockNote: async (noteId: string, password: string) => {
const { data } = await axiosPrivate.post(`/notes/${noteId}/unlock`, { password })
return data
},
}
// Text to Speech Service
export const ttsService = {
generateSpeech: async (text: string, voice = 'Idera', responseFormat = 'mp3'): Promise<Blob> => {
const { data } = await axiosPrivate.post(
'/tts',
{ text, voice, response_format: responseFormat },
{ responseType: 'blob' }
)
return data
},
getSpeechUrl: (text: string, voice = 'Idera', responseFormat = 'mp3'): string => {
const token = localStorage.getItem('authToken') || ''
const params = new URLSearchParams({
text,
voice,
response_format: responseFormat,
})
if (token) {
params.append('token', token)
}
return `${API_BASE_URL}/tts?${params.toString()}`
},
}