"""HTML page favorites API. A favorite bookmarks a generated ``scheduled_html_pages`` row and stores a reusable style/layout summary (extracted via GLM5 at favorite time) so a later ``html_page`` task can be generated "in the style of" the favorite. All routes are owner-scoped; the underlying logic lives on ``ScheduledTaskService`` so GLM5 access and the page store stay in one place. """ from __future__ import annotations from typing import Any from fastapi import APIRouter, HTTPException, Request from pydantic import BaseModel, Field from app.gateway.deps import get_current_user # Importing the ORM models here (at app-module load, before the lifespan runs # ``Base.metadata.create_all``) registers ``scheduled_html_pages`` + # ``html_page_favorites`` with the metadata so the tables are auto-created on # startup. Without this the store's models are only imported later (inside # ``langgraph_runtime``), after create_all has already run — yielding # "no such table: html_page_favorites". from deerflow.persistence.html_pages.model import ( # noqa: F401 HtmlPageFavoriteRow, ScheduledHtmlPageRow, ) from deerflow.runtime.scheduler import get_scheduled_task_service from deerflow.runtime.scheduler.service import DuplicateFavoriteError router = APIRouter(prefix="/api/html-page-favorites", tags=["html-page-favorites"]) class HtmlPageFavoriteCreateRequest(BaseModel): page_id: str = Field(..., description="scheduled_html_pages.id to bookmark") title: str | None = None description: str | None = None tags: list[str] | None = None async def _user_id(request: Request) -> str: user_id = await get_current_user(request) if not user_id: raise HTTPException(status_code=401, detail="Authentication required") return user_id @router.get("") async def list_html_page_favorites(request: Request) -> list[dict[str, Any]]: service = get_scheduled_task_service() user_id = await _user_id(request) return await service.list_html_page_favorites(user_id) @router.post("") async def create_html_page_favorite(body: HtmlPageFavoriteCreateRequest, request: Request) -> dict[str, Any]: service = get_scheduled_task_service() user_id = await _user_id(request) try: favorite = await service.create_html_page_favorite( user_id, body.page_id, title=body.title, description=body.description, tags=body.tags, ) except DuplicateFavoriteError: raise HTTPException(status_code=409, detail="该页面已收藏,请勿重复收藏") if favorite is None: raise HTTPException(status_code=404, detail="页面不存在或无权收藏") return favorite @router.get("/{favorite_id}") async def get_html_page_favorite(favorite_id: str, request: Request) -> dict[str, Any]: service = get_scheduled_task_service() user_id = await _user_id(request) favorite = await service.get_html_page_favorite(user_id, favorite_id) if favorite is None: raise HTTPException(status_code=404, detail=f"Favorite {favorite_id} not found") return favorite @router.delete("/{favorite_id}") async def delete_html_page_favorite(favorite_id: str, request: Request) -> dict[str, Any]: service = get_scheduled_task_service() user_id = await _user_id(request) deleted = await service.delete_html_page_favorite(user_id, favorite_id) if not deleted: raise HTTPException(status_code=404, detail=f"Favorite {favorite_id} not found") return {"success": True}