feat: add snapshot types and API services

This commit is contained in:
CREDO23 2026-01-30 14:19:56 +02:00
parent fb73a2e69f
commit 6aff69f4ec
4 changed files with 92 additions and 66 deletions

View file

@ -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<typeof togglePublicShareRequest>;
export type TogglePublicShareResponse = z.infer<typeof togglePublicShareResponse>;
export type SnapshotInfo = z.infer<typeof snapshotInfo>;
export type CreateSnapshotRequest = z.infer<typeof createSnapshotRequest>;
export type CreateSnapshotResponse = z.infer<typeof createSnapshotResponse>;
export type ListSnapshotsRequest = z.infer<typeof listSnapshotsRequest>;
export type ListSnapshotsResponse = z.infer<typeof listSnapshotsResponse>;
export type DeleteSnapshotRequest = z.infer<typeof deleteSnapshotRequest>;

View file

@ -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<typeof getPublicChatRequest>;
export type GetPublicChatResponse = z.infer<typeof getPublicChatResponse>;
export type ClonePublicChatRequest = z.infer<typeof clonePublicChatRequest>;
export type ClonePublicChatResponse = z.infer<typeof clonePublicChatResponse>;
export type CompleteCloneRequest = z.infer<typeof completeCloneRequest>;
export type CompleteCloneResponse = z.infer<typeof completeCloneResponse>;

View file

@ -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<TogglePublicShareResponse> => {
const parsed = togglePublicShareRequest.safeParse(request);
createSnapshot = async (request: CreateSnapshotRequest): Promise<CreateSnapshotResponse> => {
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<ListSnapshotsResponse> => {
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<void> => {
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}`
);
};
}

View file

@ -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<GetPublicChatResponse> => {
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<ClonePublicChatResponse> => {
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<CompleteCloneResponse> => {
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();