feat: use frontend URL for public share links

This commit is contained in:
CREDO23 2026-02-02 15:36:59 +02:00
parent 0bcd7505fb
commit 3821630404
3 changed files with 76 additions and 14 deletions

View file

@ -739,7 +739,6 @@ async def update_thread_visibility(
@router.post("/threads/{thread_id}/snapshots", response_model=SnapshotCreateResponse)
async def create_thread_snapshot(
thread_id: int,
request: Request,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
):
@ -747,23 +746,19 @@ async def create_thread_snapshot(
Create a public snapshot of the thread.
Returns existing snapshot URL if content unchanged (deduplication).
Only the thread owner can create snapshots.
"""
from app.services.public_chat_service import create_snapshot
base_url = str(request.base_url).rstrip("/")
return await create_snapshot(
session=session,
thread_id=thread_id,
user=user,
base_url=base_url,
)
@router.get("/threads/{thread_id}/snapshots", response_model=SnapshotListResponse)
async def list_thread_snapshots(
thread_id: int,
request: Request,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
):
@ -774,13 +769,11 @@ async def list_thread_snapshots(
"""
from app.services.public_chat_service import list_snapshots_for_thread
base_url = str(request.base_url).rstrip("/")
return SnapshotListResponse(
snapshots=await list_snapshots_for_thread(
session=session,
thread_id=thread_id,
user=user,
base_url=base_url,
)
)

View file

@ -236,6 +236,24 @@ class SnapshotListResponse(BaseModel):
snapshots: list[SnapshotInfo]
class SearchSpaceSnapshotInfo(BaseModel):
"""Snapshot info with thread context for search space listing."""
id: int
share_token: str
public_url: str
created_at: datetime
message_count: int
thread_id: int
thread_title: str
class SearchSpaceSnapshotListResponse(BaseModel):
"""List of all snapshots in a search space."""
snapshots: list[SearchSpaceSnapshotInfo]
# =============================================================================
# Public Chat View Schemas (for unauthenticated access)
# =============================================================================

View file

@ -161,7 +161,6 @@ async def create_snapshot(
session: AsyncSession,
thread_id: int,
user: User,
base_url: str,
) -> dict:
"""
Create a public snapshot of a chat thread.
@ -169,6 +168,9 @@ async def create_snapshot(
Returns existing snapshot if content unchanged (same hash).
Returns new snapshot with unique URL if content changed.
"""
from app.config import config
frontend_url = (config.NEXT_FRONTEND_URL or "").rstrip("/")
result = await session.execute(
select(NewChatThread)
.options(selectinload(NewChatThread.messages))
@ -250,7 +252,7 @@ async def create_snapshot(
return {
"snapshot_id": existing.id,
"share_token": existing.share_token,
"public_url": f"{base_url}/public/{existing.share_token}",
"public_url": f"{frontend_url}/public/{existing.share_token}",
"is_new": False,
}
@ -283,7 +285,7 @@ async def create_snapshot(
return {
"snapshot_id": snapshot.id,
"share_token": snapshot.share_token,
"public_url": f"{base_url}/public/{snapshot.share_token}",
"public_url": f"{frontend_url}/public/{snapshot.share_token}",
"is_new": True,
}
@ -352,10 +354,10 @@ async def list_snapshots_for_thread(
session: AsyncSession,
thread_id: int,
user: User,
base_url: str,
) -> list[dict]:
"""List all public snapshots for a thread."""
# Verify ownership
from app.config import config
result = await session.execute(
select(NewChatThread).filter(NewChatThread.id == thread_id)
)
@ -370,7 +372,6 @@ async def list_snapshots_for_thread(
detail="Only the creator can view snapshots",
)
# Get snapshots
result = await session.execute(
select(PublicChatSnapshot)
.filter(PublicChatSnapshot.thread_id == thread_id)
@ -378,11 +379,13 @@ async def list_snapshots_for_thread(
)
snapshots = result.scalars().all()
frontend_url = (config.NEXT_FRONTEND_URL or "").rstrip("/")
return [
{
"id": s.id,
"share_token": s.share_token,
"public_url": f"{base_url}/public/{s.share_token}",
"public_url": f"{frontend_url}/public/{s.share_token}",
"created_at": s.created_at.isoformat() if s.created_at else None,
"message_count": len(s.message_ids) if s.message_ids else 0,
}
@ -390,6 +393,54 @@ async def list_snapshots_for_thread(
]
async def list_snapshots_for_search_space(
session: AsyncSession,
search_space_id: int,
user: User,
) -> list[dict]:
"""List all public snapshots for a search space."""
from app.config import config
await check_permission(
session,
user,
search_space_id,
Permission.PUBLIC_SHARING_VIEW.value,
"You don't have permission to view public share links",
)
result = await session.execute(
select(PublicChatSnapshot)
.join(NewChatThread, PublicChatSnapshot.thread_id == NewChatThread.id)
.filter(NewChatThread.search_space_id == search_space_id)
.order_by(PublicChatSnapshot.created_at.desc())
)
snapshots = result.scalars().all()
snapshot_thread_ids = [s.thread_id for s in snapshots]
thread_result = await session.execute(
select(NewChatThread.id, NewChatThread.title).filter(
NewChatThread.id.in_(snapshot_thread_ids)
)
)
thread_titles = {row[0]: row[1] for row in thread_result.fetchall()}
frontend_url = (config.NEXT_FRONTEND_URL or "").rstrip("/")
return [
{
"id": s.id,
"share_token": s.share_token,
"public_url": f"{frontend_url}/public/{s.share_token}",
"created_at": s.created_at.isoformat() if s.created_at else None,
"message_count": len(s.message_ids) if s.message_ids else 0,
"thread_id": s.thread_id,
"thread_title": thread_titles.get(s.thread_id, "Untitled"),
}
for s in snapshots
]
# =============================================================================
# Snapshot Deletion
# =============================================================================