diff --git a/surfsense_web/contracts/types/chat-threads.types.ts b/surfsense_web/contracts/types/chat-threads.types.ts index e5ca183bd..f761df0fd 100644 --- a/surfsense_web/contracts/types/chat-threads.types.ts +++ b/surfsense_web/contracts/types/chat-threads.types.ts @@ -1,19 +1,51 @@ import { z } from "zod"; /** - * Toggle public share + * Snapshot info */ -export const togglePublicShareRequest = z.object({ - thread_id: z.number(), - enabled: z.boolean(), +export const snapshotInfo = z.object({ + id: z.number(), + share_token: z.string(), + created_at: z.string(), }); -export const togglePublicShareResponse = z.object({ - enabled: z.boolean(), - public_url: z.string().nullable(), - share_token: z.string().nullable(), +/** + * Create snapshot + */ +export const createSnapshotRequest = z.object({ + thread_id: z.number(), +}); + +export const createSnapshotResponse = z.object({ + id: z.number(), + share_token: z.string(), + public_url: z.string(), + is_new: z.boolean(), +}); + +/** + * List snapshots + */ +export const listSnapshotsRequest = z.object({ + thread_id: z.number(), +}); + +export const listSnapshotsResponse = z.object({ + snapshots: z.array(snapshotInfo), +}); + +/** + * Delete snapshot + */ +export const deleteSnapshotRequest = z.object({ + thread_id: z.number(), + snapshot_id: z.number(), }); // Type exports -export type TogglePublicShareRequest = z.infer; -export type TogglePublicShareResponse = z.infer; +export type SnapshotInfo = z.infer; +export type CreateSnapshotRequest = z.infer; +export type CreateSnapshotResponse = z.infer; +export type ListSnapshotsRequest = z.infer; +export type ListSnapshotsResponse = z.infer; +export type DeleteSnapshotRequest = z.infer; diff --git a/surfsense_web/contracts/types/public-chat.types.ts b/surfsense_web/contracts/types/public-chat.types.ts index f7aea5969..11c338dfd 100644 --- a/surfsense_web/contracts/types/public-chat.types.ts +++ b/surfsense_web/contracts/types/public-chat.types.ts @@ -39,7 +39,7 @@ export const getPublicChatResponse = z.object({ }); /** - * Clone public chat (init) + * Clone public chat */ export const clonePublicChatRequest = z.object({ share_token: z.string(), @@ -48,19 +48,6 @@ export const clonePublicChatRequest = z.object({ export const clonePublicChatResponse = z.object({ thread_id: z.number(), search_space_id: z.number(), - share_token: z.string(), -}); - -/** - * Complete clone - */ -export const completeCloneRequest = z.object({ - thread_id: z.number(), -}); - -export const completeCloneResponse = z.object({ - status: z.string(), - message_count: z.number(), }); // Type exports @@ -71,5 +58,3 @@ export type GetPublicChatRequest = z.infer; export type GetPublicChatResponse = z.infer; export type ClonePublicChatRequest = z.infer; export type ClonePublicChatResponse = z.infer; -export type CompleteCloneRequest = z.infer; -export type CompleteCloneResponse = z.infer; diff --git a/surfsense_web/lib/apis/chat-threads-api.service.ts b/surfsense_web/lib/apis/chat-threads-api.service.ts index 9ad241c42..144defcb2 100644 --- a/surfsense_web/lib/apis/chat-threads-api.service.ts +++ b/surfsense_web/lib/apis/chat-threads-api.service.ts @@ -1,31 +1,66 @@ import { - type TogglePublicShareRequest, - type TogglePublicShareResponse, - togglePublicShareRequest, - togglePublicShareResponse, + type CreateSnapshotRequest, + type CreateSnapshotResponse, + createSnapshotRequest, + createSnapshotResponse, + type DeleteSnapshotRequest, + deleteSnapshotRequest, + type ListSnapshotsRequest, + type ListSnapshotsResponse, + listSnapshotsRequest, + listSnapshotsResponse, } from "@/contracts/types/chat-threads.types"; import { ValidationError } from "../error"; import { baseApiService } from "./base-api.service"; class ChatThreadsApiService { /** - * Toggle public sharing for a thread. - * Requires authentication. + * Create a public snapshot for a thread. */ - togglePublicShare = async ( - request: TogglePublicShareRequest - ): Promise => { - const parsed = togglePublicShareRequest.safeParse(request); + createSnapshot = async (request: CreateSnapshotRequest): Promise => { + const parsed = createSnapshotRequest.safeParse(request); if (!parsed.success) { const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", "); throw new ValidationError(`Invalid request: ${errorMessage}`); } - return baseApiService.patch( - `/api/v1/threads/${parsed.data.thread_id}/public-share`, - togglePublicShareResponse, - { body: { enabled: parsed.data.enabled } } + return baseApiService.post( + `/api/v1/threads/${parsed.data.thread_id}/snapshots`, + createSnapshotResponse + ); + }; + + /** + * List all snapshots for a thread. + */ + listSnapshots = async (request: ListSnapshotsRequest): Promise => { + const parsed = listSnapshotsRequest.safeParse(request); + + if (!parsed.success) { + const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", "); + throw new ValidationError(`Invalid request: ${errorMessage}`); + } + + return baseApiService.get( + `/api/v1/threads/${parsed.data.thread_id}/snapshots`, + listSnapshotsResponse + ); + }; + + /** + * Delete a specific snapshot. + */ + deleteSnapshot = async (request: DeleteSnapshotRequest): Promise => { + const parsed = deleteSnapshotRequest.safeParse(request); + + if (!parsed.success) { + const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", "); + throw new ValidationError(`Invalid request: ${errorMessage}`); + } + + await baseApiService.delete( + `/api/v1/threads/${parsed.data.thread_id}/snapshots/${parsed.data.snapshot_id}` ); }; } diff --git a/surfsense_web/lib/apis/public-chat-api.service.ts b/surfsense_web/lib/apis/public-chat-api.service.ts index 49b1bd686..54fde2f46 100644 --- a/surfsense_web/lib/apis/public-chat-api.service.ts +++ b/surfsense_web/lib/apis/public-chat-api.service.ts @@ -1,12 +1,8 @@ import { type ClonePublicChatRequest, type ClonePublicChatResponse, - type CompleteCloneRequest, - type CompleteCloneResponse, clonePublicChatRequest, clonePublicChatResponse, - completeCloneRequest, - completeCloneResponse, type GetPublicChatRequest, type GetPublicChatResponse, getPublicChatRequest, @@ -18,7 +14,6 @@ import { baseApiService } from "./base-api.service"; class PublicChatApiService { /** * Get a public chat by share token. - * No authentication required. */ getPublicChat = async (request: GetPublicChatRequest): Promise => { const parsed = getPublicChatRequest.safeParse(request); @@ -33,8 +28,6 @@ class PublicChatApiService { /** * Clone a public chat to the user's account. - * Creates an empty thread and returns thread_id for redirect. - * Requires authentication. */ clonePublicChat = async (request: ClonePublicChatRequest): Promise => { const parsed = clonePublicChatRequest.safeParse(request); @@ -49,25 +42,6 @@ class PublicChatApiService { clonePublicChatResponse ); }; - - /** - * Complete the clone by copying messages and podcasts. - * Called from the chat page after redirect. - * Requires authentication. - */ - completeClone = async (request: CompleteCloneRequest): Promise => { - const parsed = completeCloneRequest.safeParse(request); - - if (!parsed.success) { - const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - return baseApiService.post( - `/api/v1/threads/${parsed.data.thread_id}/complete-clone`, - completeCloneResponse - ); - }; } export const publicChatApiService = new PublicChatApiService();