refactor: rename snapshot types to PublicChatSnapshot prefix

This commit is contained in:
CREDO23 2026-02-02 16:05:23 +02:00
parent e62e4faaa5
commit d890c562d4
9 changed files with 95 additions and 91 deletions

View file

@ -45,9 +45,9 @@ from app.schemas.new_chat import (
NewChatThreadUpdate, NewChatThreadUpdate,
NewChatThreadVisibilityUpdate, NewChatThreadVisibilityUpdate,
NewChatThreadWithMessages, NewChatThreadWithMessages,
PublicChatSnapshotCreateResponse,
PublicChatSnapshotListResponse,
RegenerateRequest, RegenerateRequest,
SnapshotCreateResponse,
SnapshotListResponse,
ThreadHistoryLoadResponse, ThreadHistoryLoadResponse,
ThreadListItem, ThreadListItem,
ThreadListResponse, ThreadListResponse,
@ -736,7 +736,7 @@ async def update_thread_visibility(
# ============================================================================= # =============================================================================
@router.post("/threads/{thread_id}/snapshots", response_model=SnapshotCreateResponse) @router.post("/threads/{thread_id}/snapshots", response_model=PublicChatSnapshotCreateResponse)
async def create_thread_snapshot( async def create_thread_snapshot(
thread_id: int, thread_id: int,
session: AsyncSession = Depends(get_async_session), session: AsyncSession = Depends(get_async_session),
@ -756,7 +756,7 @@ async def create_thread_snapshot(
) )
@router.get("/threads/{thread_id}/snapshots", response_model=SnapshotListResponse) @router.get("/threads/{thread_id}/snapshots", response_model=PublicChatSnapshotListResponse)
async def list_thread_snapshots( async def list_thread_snapshots(
thread_id: int, thread_id: int,
session: AsyncSession = Depends(get_async_session), session: AsyncSession = Depends(get_async_session),
@ -769,7 +769,7 @@ async def list_thread_snapshots(
""" """
from app.services.public_chat_service import list_snapshots_for_thread from app.services.public_chat_service import list_snapshots_for_thread
return SnapshotListResponse( return PublicChatSnapshotListResponse(
snapshots=await list_snapshots_for_thread( snapshots=await list_snapshots_for_thread(
session=session, session=session,
thread_id=thread_id, thread_id=thread_id,

View file

@ -514,7 +514,7 @@ async def list_search_space_snapshots(
Requires PUBLIC_SHARING_VIEW permission. Requires PUBLIC_SHARING_VIEW permission.
""" """
from app.schemas.new_chat import SearchSpaceSnapshotListResponse from app.schemas.new_chat import PublicChatSnapshotsBySpaceResponse
from app.services.public_chat_service import list_snapshots_for_search_space from app.services.public_chat_service import list_snapshots_for_search_space
snapshots = await list_snapshots_for_search_space( snapshots = await list_snapshots_for_search_space(
@ -522,4 +522,4 @@ async def list_search_space_snapshots(
search_space_id=search_space_id, search_space_id=search_space_id,
user=user, user=user,
) )
return SearchSpaceSnapshotListResponse(snapshots=snapshots) return PublicChatSnapshotsBySpaceResponse(snapshots=snapshots)

View file

@ -211,17 +211,17 @@ class RegenerateRequest(BaseModel):
# ============================================================================= # =============================================================================
class SnapshotCreateResponse(BaseModel): class PublicChatSnapshotCreateResponse(BaseModel):
"""Response after creating a public snapshot.""" """Response after creating a public chat snapshot."""
snapshot_id: int snapshot_id: int
share_token: str share_token: str
public_url: str public_url: str
is_new: bool # False if existing snapshot returned (same content) is_new: bool
class SnapshotInfo(BaseModel): class PublicChatSnapshotInfo(BaseModel):
"""Info about a single snapshot.""" """Info about a single public chat snapshot."""
id: int id: int
share_token: str share_token: str
@ -230,14 +230,14 @@ class SnapshotInfo(BaseModel):
message_count: int message_count: int
class SnapshotListResponse(BaseModel): class PublicChatSnapshotListResponse(BaseModel):
"""List of snapshots for a thread.""" """List of public chat snapshots for a thread."""
snapshots: list[SnapshotInfo] snapshots: list[PublicChatSnapshotInfo]
class SearchSpaceSnapshotInfo(BaseModel): class PublicChatSnapshotDetail(BaseModel):
"""Snapshot info with thread context for search space listing.""" """Public chat snapshot with thread context."""
id: int id: int
share_token: str share_token: str
@ -248,10 +248,10 @@ class SearchSpaceSnapshotInfo(BaseModel):
thread_title: str thread_title: str
class SearchSpaceSnapshotListResponse(BaseModel): class PublicChatSnapshotsBySpaceResponse(BaseModel):
"""List of all snapshots in a search space.""" """List of public chat snapshots for a search space."""
snapshots: list[SearchSpaceSnapshotInfo] snapshots: list[PublicChatSnapshotDetail]
# ============================================================================= # =============================================================================

View file

@ -1,17 +1,16 @@
import { atomWithMutation } from "jotai-tanstack-query"; import { atomWithMutation } from "jotai-tanstack-query";
import { toast } from "sonner"; import { toast } from "sonner";
import type { import type {
CreateSnapshotRequest, PublicChatSnapshotCreateRequest,
CreateSnapshotResponse, PublicChatSnapshotCreateResponse,
} from "@/contracts/types/chat-threads.types"; } from "@/contracts/types/chat-threads.types";
import { chatThreadsApiService } from "@/lib/apis/chat-threads-api.service"; import { chatThreadsApiService } from "@/lib/apis/chat-threads-api.service";
export const createSnapshotMutationAtom = atomWithMutation(() => ({ export const createPublicChatSnapshotMutationAtom = atomWithMutation(() => ({
mutationFn: async (request: CreateSnapshotRequest) => { mutationFn: async (request: PublicChatSnapshotCreateRequest) => {
return chatThreadsApiService.createSnapshot(request); return chatThreadsApiService.createPublicChatSnapshot(request);
}, },
onSuccess: (response: CreateSnapshotResponse) => { onSuccess: (response: PublicChatSnapshotCreateResponse) => {
// Construct URL using frontend origin (backend returns its own URL which differs)
const publicUrl = `${window.location.origin}/public/${response.share_token}`; const publicUrl = `${window.location.origin}/public/${response.share_token}`;
navigator.clipboard.writeText(publicUrl); navigator.clipboard.writeText(publicUrl);
if (response.is_new) { if (response.is_new) {

View file

@ -3,18 +3,18 @@ import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-quer
import { chatThreadsApiService } from "@/lib/apis/chat-threads-api.service"; import { chatThreadsApiService } from "@/lib/apis/chat-threads-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cacheKeys } from "@/lib/query-client/cache-keys";
export const searchSpaceSnapshotsAtom = atomWithQuery((get) => { export const publicChatSnapshotsAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom); const searchSpaceId = get(activeSearchSpaceIdAtom);
return { return {
queryKey: cacheKeys.snapshots.bySearchSpace(Number(searchSpaceId) || 0), queryKey: cacheKeys.publicChatSnapshots.bySearchSpace(Number(searchSpaceId) || 0),
enabled: !!searchSpaceId, enabled: !!searchSpaceId,
staleTime: 5 * 60 * 1000, staleTime: 5 * 60 * 1000,
queryFn: async () => { queryFn: async () => {
if (!searchSpaceId) { if (!searchSpaceId) {
return { snapshots: [] }; return { snapshots: [] };
} }
return chatThreadsApiService.listSearchSpaceSnapshots({ return chatThreadsApiService.listPublicChatSnapshotsForSearchSpace({
search_space_id: Number(searchSpaceId), search_space_id: Number(searchSpaceId),
}); });
}, },

View file

@ -5,7 +5,7 @@ import { useAtomValue, useSetAtom } from "jotai";
import { Globe, User, Users } from "lucide-react"; import { Globe, User, Users } from "lucide-react";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { createSnapshotMutationAtom } from "@/atoms/chat/chat-thread-mutation.atoms"; import { createPublicChatSnapshotMutationAtom } from "@/atoms/chat/chat-thread-mutation.atoms";
import { currentThreadAtom, setThreadVisibilityAtom } from "@/atoms/chat/current-thread.atom"; import { currentThreadAtom, setThreadVisibilityAtom } from "@/atoms/chat/current-thread.atom";
import { myAccessAtom } from "@/atoms/members/members-query.atoms"; import { myAccessAtom } from "@/atoms/members/members-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -54,7 +54,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
// Snapshot creation mutation // Snapshot creation mutation
const { mutateAsync: createSnapshot, isPending: isCreatingSnapshot } = useAtomValue( const { mutateAsync: createSnapshot, isPending: isCreatingSnapshot } = useAtomValue(
createSnapshotMutationAtom createPublicChatSnapshotMutationAtom
); );
// Permission check for public sharing // Permission check for public sharing

View file

@ -1,9 +1,9 @@
import { z } from "zod"; import { z } from "zod";
/** /**
* Snapshot info * Public chat snapshot info
*/ */
export const snapshotInfo = z.object({ export const publicChatSnapshotInfo = z.object({
id: z.number(), id: z.number(),
share_token: z.string(), share_token: z.string(),
public_url: z.string(), public_url: z.string(),
@ -12,13 +12,13 @@ export const snapshotInfo = z.object({
}); });
/** /**
* Create snapshot * Create public chat snapshot
*/ */
export const createSnapshotRequest = z.object({ export const publicChatSnapshotCreateRequest = z.object({
thread_id: z.number(), thread_id: z.number(),
}); });
export const createSnapshotResponse = z.object({ export const publicChatSnapshotCreateResponse = z.object({
snapshot_id: z.number(), snapshot_id: z.number(),
share_token: z.string(), share_token: z.string(),
public_url: z.string(), public_url: z.string(),
@ -26,28 +26,28 @@ export const createSnapshotResponse = z.object({
}); });
/** /**
* List snapshots * List public chat snapshots for thread
*/ */
export const listSnapshotsRequest = z.object({ export const publicChatSnapshotListRequest = z.object({
thread_id: z.number(), thread_id: z.number(),
}); });
export const listSnapshotsResponse = z.object({ export const publicChatSnapshotListResponse = z.object({
snapshots: z.array(snapshotInfo), snapshots: z.array(publicChatSnapshotInfo),
}); });
/** /**
* Delete snapshot * Delete public chat snapshot
*/ */
export const deleteSnapshotRequest = z.object({ export const publicChatSnapshotDeleteRequest = z.object({
thread_id: z.number(), thread_id: z.number(),
snapshot_id: z.number(), snapshot_id: z.number(),
}); });
/** /**
* Search space snapshot info (includes thread context) * Public chat snapshot with thread context
*/ */
export const searchSpaceSnapshotInfo = z.object({ export const publicChatSnapshotDetail = z.object({
id: z.number(), id: z.number(),
share_token: z.string(), share_token: z.string(),
public_url: z.string(), public_url: z.string(),
@ -58,23 +58,23 @@ export const searchSpaceSnapshotInfo = z.object({
}); });
/** /**
* List snapshots for search space * List public chat snapshots for search space
*/ */
export const listSearchSpaceSnapshotsRequest = z.object({ export const publicChatSnapshotsBySpaceRequest = z.object({
search_space_id: z.number(), search_space_id: z.number(),
}); });
export const listSearchSpaceSnapshotsResponse = z.object({ export const publicChatSnapshotsBySpaceResponse = z.object({
snapshots: z.array(searchSpaceSnapshotInfo), snapshots: z.array(publicChatSnapshotDetail),
}); });
// Type exports // Type exports
export type SnapshotInfo = z.infer<typeof snapshotInfo>; export type PublicChatSnapshotInfo = z.infer<typeof publicChatSnapshotInfo>;
export type CreateSnapshotRequest = z.infer<typeof createSnapshotRequest>; export type PublicChatSnapshotCreateRequest = z.infer<typeof publicChatSnapshotCreateRequest>;
export type CreateSnapshotResponse = z.infer<typeof createSnapshotResponse>; export type PublicChatSnapshotCreateResponse = z.infer<typeof publicChatSnapshotCreateResponse>;
export type ListSnapshotsRequest = z.infer<typeof listSnapshotsRequest>; export type PublicChatSnapshotListRequest = z.infer<typeof publicChatSnapshotListRequest>;
export type ListSnapshotsResponse = z.infer<typeof listSnapshotsResponse>; export type PublicChatSnapshotListResponse = z.infer<typeof publicChatSnapshotListResponse>;
export type DeleteSnapshotRequest = z.infer<typeof deleteSnapshotRequest>; export type PublicChatSnapshotDeleteRequest = z.infer<typeof publicChatSnapshotDeleteRequest>;
export type SearchSpaceSnapshotInfo = z.infer<typeof searchSpaceSnapshotInfo>; export type PublicChatSnapshotDetail = z.infer<typeof publicChatSnapshotDetail>;
export type ListSearchSpaceSnapshotsRequest = z.infer<typeof listSearchSpaceSnapshotsRequest>; export type PublicChatSnapshotsBySpaceRequest = z.infer<typeof publicChatSnapshotsBySpaceRequest>;
export type ListSearchSpaceSnapshotsResponse = z.infer<typeof listSearchSpaceSnapshotsResponse>; export type PublicChatSnapshotsBySpaceResponse = z.infer<typeof publicChatSnapshotsBySpaceResponse>;

View file

@ -1,28 +1,30 @@
import { import {
type CreateSnapshotRequest, type PublicChatSnapshotCreateRequest,
type CreateSnapshotResponse, type PublicChatSnapshotCreateResponse,
createSnapshotRequest, type PublicChatSnapshotDeleteRequest,
createSnapshotResponse, type PublicChatSnapshotListRequest,
type DeleteSnapshotRequest, type PublicChatSnapshotListResponse,
deleteSnapshotRequest, type PublicChatSnapshotsBySpaceRequest,
type ListSearchSpaceSnapshotsRequest, type PublicChatSnapshotsBySpaceResponse,
type ListSearchSpaceSnapshotsResponse, publicChatSnapshotCreateRequest,
type ListSnapshotsRequest, publicChatSnapshotCreateResponse,
type ListSnapshotsResponse, publicChatSnapshotDeleteRequest,
listSearchSpaceSnapshotsRequest, publicChatSnapshotListRequest,
listSearchSpaceSnapshotsResponse, publicChatSnapshotListResponse,
listSnapshotsRequest, publicChatSnapshotsBySpaceRequest,
listSnapshotsResponse, publicChatSnapshotsBySpaceResponse,
} from "@/contracts/types/chat-threads.types"; } from "@/contracts/types/chat-threads.types";
import { ValidationError } from "../error"; import { ValidationError } from "../error";
import { baseApiService } from "./base-api.service"; import { baseApiService } from "./base-api.service";
class ChatThreadsApiService { class ChatThreadsApiService {
/** /**
* Create a public snapshot for a thread. * Create a public chat snapshot for a thread.
*/ */
createSnapshot = async (request: CreateSnapshotRequest): Promise<CreateSnapshotResponse> => { createPublicChatSnapshot = async (
const parsed = createSnapshotRequest.safeParse(request); request: PublicChatSnapshotCreateRequest
): Promise<PublicChatSnapshotCreateResponse> => {
const parsed = publicChatSnapshotCreateRequest.safeParse(request);
if (!parsed.success) { if (!parsed.success) {
const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", "); const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", ");
@ -31,15 +33,17 @@ class ChatThreadsApiService {
return baseApiService.post( return baseApiService.post(
`/api/v1/threads/${parsed.data.thread_id}/snapshots`, `/api/v1/threads/${parsed.data.thread_id}/snapshots`,
createSnapshotResponse publicChatSnapshotCreateResponse
); );
}; };
/** /**
* List all snapshots for a thread. * List all public chat snapshots for a thread.
*/ */
listSnapshots = async (request: ListSnapshotsRequest): Promise<ListSnapshotsResponse> => { listPublicChatSnapshots = async (
const parsed = listSnapshotsRequest.safeParse(request); request: PublicChatSnapshotListRequest
): Promise<PublicChatSnapshotListResponse> => {
const parsed = publicChatSnapshotListRequest.safeParse(request);
if (!parsed.success) { if (!parsed.success) {
const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", "); const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", ");
@ -48,15 +52,15 @@ class ChatThreadsApiService {
return baseApiService.get( return baseApiService.get(
`/api/v1/threads/${parsed.data.thread_id}/snapshots`, `/api/v1/threads/${parsed.data.thread_id}/snapshots`,
listSnapshotsResponse publicChatSnapshotListResponse
); );
}; };
/** /**
* Delete a specific snapshot. * Delete a public chat snapshot.
*/ */
deleteSnapshot = async (request: DeleteSnapshotRequest): Promise<void> => { deletePublicChatSnapshot = async (request: PublicChatSnapshotDeleteRequest): Promise<void> => {
const parsed = deleteSnapshotRequest.safeParse(request); const parsed = publicChatSnapshotDeleteRequest.safeParse(request);
if (!parsed.success) { if (!parsed.success) {
const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", "); const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", ");
@ -69,12 +73,12 @@ class ChatThreadsApiService {
}; };
/** /**
* List all snapshots for a search space. * List all public chat snapshots for a search space.
*/ */
listSearchSpaceSnapshots = async ( listPublicChatSnapshotsForSearchSpace = async (
request: ListSearchSpaceSnapshotsRequest request: PublicChatSnapshotsBySpaceRequest
): Promise<ListSearchSpaceSnapshotsResponse> => { ): Promise<PublicChatSnapshotsBySpaceResponse> => {
const parsed = listSearchSpaceSnapshotsRequest.safeParse(request); const parsed = publicChatSnapshotsBySpaceRequest.safeParse(request);
if (!parsed.success) { if (!parsed.success) {
const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", "); const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", ");
@ -83,7 +87,7 @@ class ChatThreadsApiService {
return baseApiService.get( return baseApiService.get(
`/api/v1/searchspaces/${parsed.data.search_space_id}/snapshots`, `/api/v1/searchspaces/${parsed.data.search_space_id}/snapshots`,
listSearchSpaceSnapshotsResponse publicChatSnapshotsBySpaceResponse
); );
}; };
} }

View file

@ -82,7 +82,8 @@ export const cacheKeys = {
publicChat: { publicChat: {
byToken: (shareToken: string) => ["public-chat", shareToken] as const, byToken: (shareToken: string) => ["public-chat", shareToken] as const,
}, },
snapshots: { publicChatSnapshots: {
bySearchSpace: (searchSpaceId: number) => ["snapshots", "search-space", searchSpaceId] as const, bySearchSpace: (searchSpaceId: number) =>
["public-chat-snapshots", "search-space", searchSpaceId] as const,
}, },
}; };