"""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}