teganmosi
Deploy Aria application with fixes for database, TTS, and WebSocket (LFS tracking applied)
f6d0a88 | // @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()}` | |
| }, | |
| } | |