"""Public endpoints for accessing shared content (no authentication required).""" from datetime import datetime, timezone from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from sqlalchemy.orm import selectinload from app.core.database import get_db from app.models.tree import Tree from app.models.tree_share import TreeShare from app.schemas.tree import SharedTreeResponse router = APIRouter(prefix="/shared", tags=["shared"]) @router.get("/{share_token}", response_model=SharedTreeResponse) async def get_shared_tree( share_token: str, db: Annotated[AsyncSession, Depends(get_db)] ): """Get a tree by its share token (PUBLIC endpoint - no auth required). Returns 404 if: - Share token doesn't exist - Share token has expired - Tree is not active """ # Look up share token result = await db.execute( select(TreeShare) .options(selectinload(TreeShare.tree).selectinload(Tree.tags)) .where(TreeShare.share_token == share_token) ) tree_share = result.scalar_one_or_none() if not tree_share: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Share link not found or has been revoked" ) # Check expiration if tree_share.expires_at and tree_share.expires_at < datetime.now(timezone.utc): raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Share link has expired" ) # Check tree is active tree = tree_share.tree if not tree or not tree.is_active: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Tree not found" ) # Build response (minimal info for public access) return SharedTreeResponse( id=tree.id, name=tree.name, description=tree.description, category=tree.category, tree_structure=tree.tree_structure, tags=tree.tag_names, version=tree.version, allow_forking=tree_share.allow_forking, created_at=tree.created_at, updated_at=tree.updated_at )