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