mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-12 01:02:39 +02:00
add podcast api service
This commit is contained in:
parent
5361290315
commit
20cd1951b5
5 changed files with 54 additions and 22 deletions
|
|
@ -1,21 +1,13 @@
|
||||||
import { atom } from "jotai";
|
import { atom } from "jotai";
|
||||||
import { atomWithQuery } from "jotai-tanstack-query";
|
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 { 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 { chatsApiService } from "@/lib/apis/chats-api.service";
|
||||||
import { podcastsApiService } from "@/lib/apis/podcasts-api.service";
|
import { podcastsApiService } from "@/lib/apis/podcasts-api.service";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
|
|
||||||
type ActiveChatState = {
|
|
||||||
chatId: string | null;
|
|
||||||
chatDetails: ChatDetails | null;
|
|
||||||
podcast: Podcast | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const activeChatIdAtom = atom<string | null>(null);
|
export const activeChatIdAtom = atom<string | null>(null);
|
||||||
|
|
||||||
export const activeChatAtom = atomWithQuery<ActiveChatState>((get) => {
|
export const activeChatAtom = atomWithQuery((get) => {
|
||||||
const activeChatId = get(activeChatIdAtom);
|
const activeChatId = get(activeChatIdAtom);
|
||||||
const authToken = localStorage.getItem("surfsense_bearer_token");
|
const authToken = localStorage.getItem("surfsense_bearer_token");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ export function ChatPanelContainer() {
|
||||||
await podcastsApiService.generatePodcast(request);
|
await podcastsApiService.generatePodcast(request);
|
||||||
toast.success(`Podcast generation started!`);
|
toast.success(`Podcast generation started!`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Error generating podcast. Please log in again.");
|
toast.error("Error generating podcast. Please try again later.");
|
||||||
console.error("Error generating podcast:", error);
|
console.error("Error generating podcast:", JSON.stringify(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ export const getPodcastByChatIdRequest = z.object({
|
||||||
chat_id: z.number(),
|
chat_id: z.number(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const getPodcastByChatResponse = podcast.nullish();
|
||||||
|
|
||||||
export type GeneratePodcastRequest = z.infer<typeof generatePodcastRequest>;
|
export type GeneratePodcastRequest = z.infer<typeof generatePodcastRequest>;
|
||||||
export type GetPodcastByChatIdRequest = z.infer<typeof getPodcastByChatIdRequest>;
|
export type GetPodcastByChatIdRequest = z.infer<typeof getPodcastByChatIdRequest>;
|
||||||
|
export type GetPodcastByChatResponse = z.infer<typeof getPodcastByChatResponse>;
|
||||||
export type Podcast = z.infer<typeof podcast>;
|
export type Podcast = z.infer<typeof podcast>;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { th } from "date-fns/locale";
|
||||||
import type z from "zod";
|
import type z from "zod";
|
||||||
import { AppError, AuthenticationError, AuthorizationError, NotFoundError } from "../error";
|
import { AppError, AuthenticationError, AuthorizationError, NotFoundError } from "../error";
|
||||||
|
|
||||||
|
|
@ -50,6 +51,11 @@ class BaseApiService {
|
||||||
: unknown
|
: unknown
|
||||||
> {
|
> {
|
||||||
try {
|
try {
|
||||||
|
/**
|
||||||
|
* ----------
|
||||||
|
* REQUEST
|
||||||
|
* ----------
|
||||||
|
*/
|
||||||
const defaultOptions: RequestOptions = {
|
const defaultOptions: RequestOptions = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -68,18 +74,46 @@ class BaseApiService {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Validate the base URL
|
||||||
if (!this.baseUrl) {
|
if (!this.baseUrl) {
|
||||||
throw new AppError("Base URL is not set.");
|
throw new AppError("Base URL is not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate the bearer token
|
||||||
if (!this.bearerToken && !this.noAuthEndpoints.includes(url)) {
|
if (!this.bearerToken && !this.noAuthEndpoints.includes(url)) {
|
||||||
throw new AuthenticationError("You are not authenticated. Please login again.");
|
throw new AuthenticationError("You are not authenticated. Please login again.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Construct the full URL
|
||||||
const fullUrl = new URL(url, this.baseUrl).toString();
|
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) {
|
if (!response.ok) {
|
||||||
// biome-ignore lint/suspicious: Unknown
|
// biome-ignore lint/suspicious: Unknown
|
||||||
let data;
|
let data;
|
||||||
|
|
@ -87,12 +121,11 @@ class BaseApiService {
|
||||||
try {
|
try {
|
||||||
data = await response.json();
|
data = await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to parse response as JSON:", error);
|
console.error("Failed to parse response as JSON: ", JSON.stringify(error));
|
||||||
|
throw new AppError("Failed to parse response", response.status, response.statusText);
|
||||||
throw new AppError("Something went wrong", response.status, response.statusText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// for fastapi errors response
|
// For fastapi errors response
|
||||||
if (typeof data === "object" && "detail" in data) {
|
if (typeof data === "object" && "detail" in data) {
|
||||||
throw new AppError(data.detail, response.status, response.statusText);
|
throw new AppError(data.detail, response.status, response.statusText);
|
||||||
}
|
}
|
||||||
|
|
@ -138,13 +171,14 @@ class BaseApiService {
|
||||||
break;
|
break;
|
||||||
// Add more cases as needed
|
// Add more cases as needed
|
||||||
default:
|
default:
|
||||||
data = await response.text();
|
data = await response.json();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to parse response as JSON:", error);
|
console.error("Failed to parse response as JSON:", error);
|
||||||
throw new AppError("Failed to parse response", response.status, response.statusText);
|
throw new AppError("Failed to parse response", response.status, response.statusText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate response
|
||||||
if (responseType === ResponseType.JSON) {
|
if (responseType === ResponseType.JSON) {
|
||||||
if (!responseSchema) {
|
if (!responseSchema) {
|
||||||
return data;
|
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 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.
|
* 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;
|
return data;
|
||||||
|
|
@ -164,7 +198,7 @@ class BaseApiService {
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Request failed:", error);
|
console.error("Request failed:", JSON.stringify(error));
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ import {
|
||||||
type GetPodcastByChatIdRequest,
|
type GetPodcastByChatIdRequest,
|
||||||
generatePodcastRequest,
|
generatePodcastRequest,
|
||||||
getPodcastByChatIdRequest,
|
getPodcastByChatIdRequest,
|
||||||
|
getPodcastByChatResponse,
|
||||||
type Podcast,
|
type Podcast,
|
||||||
podcast,
|
|
||||||
} from "@/contracts/types/podcast.types";
|
} from "@/contracts/types/podcast.types";
|
||||||
import { ValidationError } from "../error";
|
import { ValidationError } from "../error";
|
||||||
import { baseApiService } from "./base-api.service";
|
import { baseApiService } from "./base-api.service";
|
||||||
|
|
@ -22,7 +22,10 @@ class PodcastsApiService {
|
||||||
throw new ValidationError(`Invalid request: ${errorMessage}`);
|
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) => {
|
generatePodcast = async (request: GeneratePodcastRequest) => {
|
||||||
|
|
@ -38,7 +41,7 @@ class PodcastsApiService {
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseApiService.post(`/api/v1/podcasts/generate`, undefined, {
|
return baseApiService.post(`/api/v1/podcasts/generate`, undefined, {
|
||||||
body: request,
|
body: parsedRequest.data,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue