diff --git a/surfsense_web/lib/apis/chats.api.ts b/surfsense_web/lib/apis/chats.api.ts index 5a4789be7..ddd1704ae 100644 --- a/surfsense_web/lib/apis/chats.api.ts +++ b/surfsense_web/lib/apis/chats.api.ts @@ -1,78 +1,167 @@ -import type { ChatDetails } from "@/app/dashboard/[search_space_id]/chats/chats-client"; +import type { + Chat, + ChatDetails, +} from "@/app/dashboard/[search_space_id]/chats/chats-client"; +import { ResearchMode } from "@/components/chat/types"; +import { Message } from "@ai-sdk/react"; export const fetchChatDetails = async ( chatId: string, authToken: string ): Promise => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/${Number( - chatId - )}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${authToken}`, - }, - } - ); - - if (!response.ok) { - throw new Error(`Failed to fetch chat details: ${response.statusText}`); + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/${Number( + chatId + )}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${authToken}`, + }, } + ); - return await response.json(); - } catch (err) { - console.error("Error fetching chat details:", err); - return null; + if (!response.ok) { + throw new Error(`Failed to fetch chat details: ${response.statusText}`); } + + return await response.json(); }; export const fetchChatsBySearchSpace = async ( searchSpaceId: string, authToken: string ): Promise => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats?search_space_id=${searchSpaceId}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${authToken}`, - }, - } - ); - if (!response.ok) { - throw new Error(`Failed to fetch chats: ${response.statusText}`); + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats?search_space_id=${searchSpaceId}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${authToken}`, + }, } + ); + if (!response.ok) { + throw new Error(`Failed to fetch chats: ${response.statusText}`); + } - return await response.json(); - } catch (err) { - console.error("Error fetching chats:", err); - return null; + return await response.json(); +}; + +export const deleteChat = async (chatId: number, authToken: string) => { + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/${chatId}`, + { + method: "DELETE", + headers: { + Authorization: `Bearer ${authToken}`, + }, + } + ); + + if (!response.ok) { + throw new Error(`Failed to delete chat: ${response.statusText}`); + } + + return await response.json(); +}; + +export const createChat = async ( + initialMessage: string, + researchMode: ResearchMode, + selectedConnectors: string[], + authToken: string, + searchSpaceId: number +): Promise => { + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${authToken}`, + }, + body: JSON.stringify({ + type: researchMode, + title: "Untitled Chat", + initial_connectors: selectedConnectors, + messages: [ + { + role: "user", + content: initialMessage, + }, + ], + search_space_id: searchSpaceId, + }), + } + ); + + if (!response.ok) { + throw new Error(`Failed to create chat: ${response.statusText}`); + } + + return await response.json(); +}; + +export const updateChat = async ( + chatId: string, + messages: Message[], + researchMode: ResearchMode, + selectedConnectors: string[], + authToken: string, + searchSpaceId: number +) => { + const userMessages = messages.filter((msg) => msg.role === "user"); + if (userMessages.length === 0) return; + + const title = userMessages[0].content; + + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/${Number( + chatId + )}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${authToken}`, + }, + body: JSON.stringify({ + type: researchMode, + title: title, + initial_connectors: selectedConnectors, + messages: messages, + search_space_id: searchSpaceId, + }), + } + ); + + if (!response.ok) { + throw new Error(`Failed to update chat: ${response.statusText}`); } }; +export const fetchChats = async ( + searchSpaceId: string, + limit: number, + skip: number, + authToken: string +) => { + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats?limit=${limit}&skip=${skip}&search_space_id=${searchSpaceId}`, + { + headers: { + Authorization: `Bearer ${authToken}`, + }, + method: "GET", + } + ); -export const deleteChat = async (chatId: number, authToken: string) => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/${chatId}`, - { - method: "DELETE", - headers: { - Authorization: `Bearer ${authToken}`, - }, - } - ); + if (!response.ok) { + throw new Error(`Failed to fetch chats: ${response.status}`); + } - if (!response.ok) { - throw new Error(`Failed to delete chat: ${response.statusText}`); - } - - } catch (err) { - console.error("Error deleting chat:", err); - } + return await response.json(); }; diff --git a/surfsense_web/lib/apis/documents.api.ts b/surfsense_web/lib/apis/documents.api.ts new file mode 100644 index 000000000..bc9822b00 --- /dev/null +++ b/surfsense_web/lib/apis/documents.api.ts @@ -0,0 +1,20 @@ +const uploadDocument = async (formData: FormData) => { + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents/fileupload`, + { + method: "POST", + headers: { + Authorization: `Bearer ${window.localStorage.getItem( + "surfsense_bearer_token" + )}`, + }, + body: formData, + } + ); + + if (!response.ok) { + throw new Error("Upload failed"); + } + + await response.json(); +}; diff --git a/surfsense_web/lib/apis/podcasts.api.ts b/surfsense_web/lib/apis/podcasts.api.ts index 0324d7066..fa9c680fe 100644 --- a/surfsense_web/lib/apis/podcasts.api.ts +++ b/surfsense_web/lib/apis/podcasts.api.ts @@ -2,49 +2,78 @@ import type { PodcastItem } from "@/app/dashboard/[search_space_id]/podcasts/pod import type { GeneratePodcastRequest } from "@/components/chat/ChatPanel/ChatPanelContainer"; export const getPodcastByChatId = async (chatId: string, authToken: string) => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/podcasts/by-chat/${Number(chatId)}`, - { - headers: { - Authorization: `Bearer ${authToken}`, - }, - method: "GET", - } - ); + const response = await fetch( + `${ + process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL + }/api/v1/podcasts/by-chat/${Number(chatId)}`, + { + headers: { + Authorization: `Bearer ${authToken}`, + }, + method: "GET", + } + ); - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.detail || "Failed to fetch podcast"); - } + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.detail || "Failed to fetch podcast"); + } - return (await response.json()) as PodcastItem | null; - } catch (err: any) { - console.error("Error fetching podcast:", err); - - return null; - } + return (await response.json()) as PodcastItem | null; }; -export const generatePodcast = async (request: GeneratePodcastRequest, authToken: string) => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/podcasts/generate/`, - { - method: "POST", - headers: { - Authorization: `Bearer ${authToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify(request), - } - ); +export const generatePodcast = async ( + request: GeneratePodcastRequest, + authToken: string +) => { + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/podcasts/generate/`, + { + method: "POST", + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(request), + } + ); - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.detail || "Failed to generate podcast"); - } - } catch (error) { - console.error("Error generating podcast:", error); - } + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.detail || "Failed to generate podcast"); + } + + return await response.json(); +}; + +export const loadPodcast = async (podcast: PodcastItem, authToken: string) => { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 30000); + + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/podcasts/${podcast.id}/stream`, + { + headers: { + Authorization: `Bearer ${authToken}`, + }, + signal: controller.signal, + } + ); + + if (!response.ok) { + throw new Error(`Failed to fetch audio stream: ${response.statusText}`); + } + + const blob = await response.blob(); + const objectUrl = URL.createObjectURL(blob); + return objectUrl; + } catch (error) { + if (error instanceof DOMException && error.name === "AbortError") { + throw new Error("Request timed out. Please try again."); + } + throw error; + } finally { + clearTimeout(timeoutId); + } }; diff --git a/surfsense_web/lib/apis/search-spaces.api.ts b/surfsense_web/lib/apis/search-spaces.api.ts index f4b2ad74d..89a437e9f 100644 --- a/surfsense_web/lib/apis/search-spaces.api.ts +++ b/surfsense_web/lib/apis/search-spaces.api.ts @@ -1,112 +1,91 @@ export const fetchSearchSpaces = async () => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`, - { - headers: { - Authorization: `Bearer ${localStorage.getItem( - "surfsense_bearer_token" - )}`, - }, - method: "GET", - } - ); - - if (!response.ok) { - throw new Error("Not authenticated"); + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`, + { + headers: { + Authorization: `Bearer ${localStorage.getItem( + "surfsense_bearer_token" + )}`, + }, + method: "GET", } + ); - const data = await response.json(); - return data; - } catch (err: any) { - console.error("Error fetching search spaces:", err); - return null; + if (!response.ok) { + throw new Error("Not authenticated"); } + + return await response.json(); }; -export const handleDeleteSearchSpace = async (id: number) => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${id}`, - { - method: "DELETE", - headers: { - Authorization: `Bearer ${localStorage.getItem( - "surfsense_bearer_token" - )}`, - }, - } - ); - - if (!response.ok) { - throw new Error("Failed to delete search space"); +export const deleteSearchSpace = async (id: number) => { + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${id}`, + { + method: "DELETE", + headers: { + Authorization: `Bearer ${localStorage.getItem( + "surfsense_bearer_token" + )}`, + }, } - } catch (error) { - console.error("Error deleting search space:", error); - return; + ); + + if (!response.ok) { + throw new Error("Failed to delete search space"); } + + return await response.json(); }; -export const handleCreateSearchSpace = async (data: { +export const createSearchSpace = async (data: { name: string; description: string; }) => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${localStorage.getItem( - "surfsense_bearer_token" - )}`, - }, - body: JSON.stringify(data), - } - ); - - if (!response.ok) { - throw new Error("Failed to create search space"); + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${localStorage.getItem( + "surfsense_bearer_token" + )}`, + }, + body: JSON.stringify(data), } + ); - const result = await response.json(); - - return result; - } catch (error) { - console.error("Error creating search space:", error); - throw error; + if (!response.ok) { + throw new Error("Failed to create search space"); } + + return await response.json(); }; export const fetchSearchSpace = async (searchSpaceId: string) => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}`, - { - headers: { - Authorization: `Bearer ${localStorage.getItem( - "surfsense_bearer_token" - )}`, - }, - method: "GET", - } - ); - - if (response.status === 401) { - // Clear token and redirect to home - localStorage.removeItem("surfsense_bearer_token"); - window.location.href = "/"; - throw new Error("Unauthorized: Redirecting to login page"); + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}`, + { + headers: { + Authorization: `Bearer ${localStorage.getItem( + "surfsense_bearer_token" + )}`, + }, + method: "GET", } + ); - if (!response.ok) { - throw new Error(`Failed to fetch search space: ${response.status}`); - } - - const data = await response.json(); - return data; - } catch (err: any) { - console.error("Error fetching search space:", err); + if (response.status === 401) { + // Clear token and redirect to home + localStorage.removeItem("surfsense_bearer_token"); + window.location.href = "/"; + throw new Error("Unauthorized: Redirecting to login page"); } + + if (!response.ok) { + throw new Error(`Failed to fetch search space: ${response.status}`); + } + + return await response.json(); };