Files

136 lines
4.3 KiB
Python
Raw Permalink Normal View History

"""Feeds router."""
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_user, get_db
from app.models.feed import Feed
from app.models.user import User
from app.schemas.common import MessageResponse, PaginatedResponse, PaginationParams
from app.schemas.feed import FeedCreate, FeedOut, FeedUpdate
router = APIRouter(prefix="/feeds", tags=["feeds"])
@router.get("", response_model=PaginatedResponse)
async def list_feeds(
pagination: PaginationParams = Depends(),
category: str | None = Query(None),
search: str | None = Query(None),
is_active: bool | None = Query(None),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""List RSS feeds with pagination and filters."""
query = select(Feed)
if category:
query = query.where(Feed.category == category)
if search:
query = query.where(
Feed.title.ilike(f"%{search}%")
| Feed.url.ilike(f"%{search}%")
| Feed.description.ilike(f"%{search}%")
)
if is_active is not None:
query = query.where(Feed.is_active == is_active)
# Get total count
count_query = select(func.count()).select_from(query.subquery())
total = (await db.execute(count_query)).scalar_one()
# Get paginated items
query = query.offset(pagination.skip).limit(pagination.limit).order_by(Feed.created_at.desc())
result = await db.execute(query)
items = result.scalars().all()
return {
"total": total,
"items": [FeedOut.model_validate(item) for item in items],
}
@router.get("/{feed_id}", response_model=FeedOut)
async def get_feed(
feed_id: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Get a single feed by ID."""
feed = await db.get(Feed, feed_id)
if not feed:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Feed not found")
return FeedOut.model_validate(feed)
@router.post("", response_model=FeedOut, status_code=status.HTTP_201_CREATED)
async def create_feed(
feed_in: FeedCreate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Create a new RSS feed."""
# Check URL uniqueness
result = await db.execute(select(Feed).where(Feed.url == str(feed_in.url)))
existing = result.scalar_one_or_none()
if existing:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Feed with this URL already exists",
)
feed = Feed(
url=str(feed_in.url),
title=feed_in.title or "",
description=feed_in.description or "",
category=feed_in.category or "",
is_active=feed_in.is_active,
fetch_interval_minutes=feed_in.fetch_interval_minutes,
priority=feed_in.priority,
parser_config=feed_in.parser_config,
proxy_policy=feed_in.proxy_policy,
)
db.add(feed)
await db.commit()
await db.refresh(feed)
return FeedOut.model_validate(feed)
@router.put("/{feed_id}", response_model=FeedOut)
async def update_feed(
feed_id: str,
feed_in: FeedUpdate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Update an existing feed."""
feed = await db.get(Feed, feed_id)
if not feed:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Feed not found")
update_data = feed_in.model_dump(exclude_unset=True)
for field, value in update_data.items():
if field == "url" and value is not None:
value = str(value)
setattr(feed, field, value)
await db.commit()
await db.refresh(feed)
return FeedOut.model_validate(feed)
@router.delete("/{feed_id}", response_model=MessageResponse)
async def delete_feed(
feed_id: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Delete a feed."""
feed = await db.get(Feed, feed_id)
if not feed:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Feed not found")
await db.delete(feed)
await db.commit()
return {"message": "Feed deleted successfully"}