diff --git a/surfsense_web/atoms/chats/chat-querie.atoms.ts b/surfsense_web/atoms/chats/chat-querie.atoms.ts index c0269d9b7..803295cf0 100644 --- a/surfsense_web/atoms/chats/chat-querie.atoms.ts +++ b/surfsense_web/atoms/chats/chat-querie.atoms.ts @@ -1,21 +1,13 @@ import { atom } from "jotai"; import { atomWithQuery } from "jotai-tanstack-query"; -import type { ChatDetails } from "@/app/dashboard/[search_space_id]/chats/chats-client"; import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom"; -import type { Podcast } from "@/contracts/types/podcast.types"; import { chatsApiService } from "@/lib/apis/chats-api.service"; import { podcastsApiService } from "@/lib/apis/podcasts-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; -type ActiveChatState = { - chatId: string | null; - chatDetails: ChatDetails | null; - podcast: Podcast | null; -}; - export const activeChatIdAtom = atom(null); -export const activeChatAtom = atomWithQuery((get) => { +export const activeChatAtom = atomWithQuery((get) => { const activeChatId = get(activeChatIdAtom); const authToken = localStorage.getItem("surfsense_bearer_token"); diff --git a/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx b/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx index 1c62d070d..93bcca5b2 100644 --- a/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx +++ b/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx @@ -27,8 +27,8 @@ export function ChatPanelContainer() { await podcastsApiService.generatePodcast(request); toast.success(`Podcast generation started!`); } catch (error) { - toast.error("Error generating podcast. Please log in again."); - console.error("Error generating podcast:", error); + toast.error("Error generating podcast. Please try again later."); + console.error("Error generating podcast:", JSON.stringify(error)); } }; diff --git a/surfsense_web/contracts/types/podcast.types.ts b/surfsense_web/contracts/types/podcast.types.ts index c037ff43e..4301e3fbb 100644 --- a/surfsense_web/contracts/types/podcast.types.ts +++ b/surfsense_web/contracts/types/podcast.types.ts @@ -22,6 +22,9 @@ export const getPodcastByChatIdRequest = z.object({ chat_id: z.number(), }); +export const getPodcastByChatResponse = podcast.nullish(); + export type GeneratePodcastRequest = z.infer; export type GetPodcastByChatIdRequest = z.infer; +export type GetPodcastByChatResponse = z.infer; export type Podcast = z.infer; diff --git a/surfsense_web/lib/apis/base-api.service.ts b/surfsense_web/lib/apis/base-api.service.ts index e396364ec..51b1f69fb 100644 --- a/surfsense_web/lib/apis/base-api.service.ts +++ b/surfsense_web/lib/apis/base-api.service.ts @@ -1,3 +1,4 @@ +import { th } from "date-fns/locale"; import type z from "zod"; import { AppError, AuthenticationError, AuthorizationError, NotFoundError } from "../error"; @@ -50,6 +51,11 @@ class BaseApiService { : unknown > { try { + /** + * ---------- + * REQUEST + * ---------- + */ const defaultOptions: RequestOptions = { headers: { "Content-Type": "application/json", @@ -68,18 +74,46 @@ class BaseApiService { }, }; + // Validate the base URL if (!this.baseUrl) { throw new AppError("Base URL is not set."); } + // Validate the bearer token if (!this.bearerToken && !this.noAuthEndpoints.includes(url)) { throw new AuthenticationError("You are not authenticated. Please login again."); } + // Construct the full URL const fullUrl = new URL(url, this.baseUrl).toString(); - const response = await fetch(fullUrl, mergedOptions); + // Prepare fetch options + const fetchOptions: RequestInit = { + method: mergedOptions.method, + headers: mergedOptions.headers, + signal: mergedOptions.signal, + }; + // Automatically stringify body if Content-Type is application/json and body is an object + if (mergedOptions.body !== undefined) { + const contentType = mergedOptions.headers?.["Content-Type"]; + if (contentType === "application/json" && typeof mergedOptions.body === "object") { + fetchOptions.body = JSON.stringify(mergedOptions.body); + } else { + // Pass body as-is for other content types (e.g., form data, already stringified) + fetchOptions.body = mergedOptions.body; + } + } + + const response = await fetch(fullUrl, fetchOptions); + + /** + * ---------- + * RESPONSE + * ---------- + */ + + // Handle errors if (!response.ok) { // biome-ignore lint/suspicious: Unknown let data; @@ -87,12 +121,11 @@ class BaseApiService { try { data = await response.json(); } catch (error) { - console.error("Failed to parse response as JSON:", error); - - throw new AppError("Something went wrong", response.status, response.statusText); + console.error("Failed to parse response as JSON: ", JSON.stringify(error)); + throw new AppError("Failed to parse response", response.status, response.statusText); } - // for fastapi errors response + // For fastapi errors response if (typeof data === "object" && "detail" in data) { throw new AppError(data.detail, response.status, response.statusText); } @@ -138,13 +171,14 @@ class BaseApiService { break; // Add more cases as needed default: - data = await response.text(); + data = await response.json(); } } catch (error) { console.error("Failed to parse response as JSON:", error); throw new AppError("Failed to parse response", response.status, response.statusText); } + // Validate response if (responseType === ResponseType.JSON) { if (!responseSchema) { return data; @@ -156,7 +190,7 @@ class BaseApiService { * This is a client side error, and should be fixed by updating the responseSchema to keep things typed. * This error should not be shown to the user , it is for dev only. */ - console.error("Invalid API response schema:", parsedData.error); + console.error(`Invalid API response schema - ${url} :`, JSON.stringify(parsedData.error)); } return data; @@ -164,7 +198,7 @@ class BaseApiService { return data; } catch (error) { - console.error("Request failed:", error); + console.error("Request failed:", JSON.stringify(error)); throw error; } } diff --git a/surfsense_web/lib/apis/podcasts-api.service.ts b/surfsense_web/lib/apis/podcasts-api.service.ts index 2142dc6e6..6e183281b 100644 --- a/surfsense_web/lib/apis/podcasts-api.service.ts +++ b/surfsense_web/lib/apis/podcasts-api.service.ts @@ -3,8 +3,8 @@ import { type GetPodcastByChatIdRequest, generatePodcastRequest, getPodcastByChatIdRequest, + getPodcastByChatResponse, type Podcast, - podcast, } from "@/contracts/types/podcast.types"; import { ValidationError } from "../error"; import { baseApiService } from "./base-api.service"; @@ -22,7 +22,10 @@ class PodcastsApiService { throw new ValidationError(`Invalid request: ${errorMessage}`); } - return baseApiService.get(`/api/v1/podcasts/by-chat/${request.chat_id}`, podcast); + return baseApiService.get( + `/api/v1/podcasts/by-chat/${request.chat_id}`, + getPodcastByChatResponse + ); }; generatePodcast = async (request: GeneratePodcastRequest) => { @@ -38,7 +41,7 @@ class PodcastsApiService { } return baseApiService.post(`/api/v1/podcasts/generate`, undefined, { - body: request, + body: parsedRequest.data, }); };