from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from app.db.session import get_db from app.services.earth_news import ( ALLOWED_NEWS_CATEGORY_KEYS, SUPPORTED_NEWS_LOCALES, REGION_ANCHORS, get_earth_news_payload, ) router = APIRouter() def _parse_categories(raw: str | None) -> set[str] | None: if raw is None or not raw.strip(): return None requested = {item.strip().lower() for item in raw.split(",") if item.strip()} invalid = sorted(requested - set(ALLOWED_NEWS_CATEGORY_KEYS)) if invalid: raise HTTPException( status_code=422, detail={ "message": "Unsupported news categories.", "invalid_categories": invalid, "allowed_categories": list(ALLOWED_NEWS_CATEGORY_KEYS), }, ) return requested or None def _parse_source_ids(raw: str | None) -> set[str] | None: if raw is None or not raw.strip(): return None return {item.strip() for item in raw.split(",") if item.strip()} or None def _parse_limit(raw: int | None) -> int: if raw is None: return 12 if raw < 1: raise HTTPException(status_code=422, detail={"message": "News limit must be greater than 0."}) return min(raw, 100) def _parse_locale(raw: str | None) -> str: if raw is None or not raw.strip(): return "zh-CN" requested = raw.strip() if requested not in SUPPORTED_NEWS_LOCALES: raise HTTPException( status_code=422, detail={ "message": "Unsupported news locale.", "invalid_locale": requested, "allowed_locales": sorted(SUPPORTED_NEWS_LOCALES), }, ) return requested @router.get("/earth-feed") async def get_earth_feed( lat: float | None = Query(None, description="Current Earth view center latitude"), lon: float | None = Query(None, description="Current Earth view center longitude"), region: str | None = Query(None, description="Explicit Earth news region for UE/client integrations"), categories: str | None = Query(None, description="Comma-separated news category keys"), sources: str | None = Query(None, description="Comma-separated news source ids"), limit: int | None = Query(None, description="Maximum news items to return, capped at 100"), locale: str | None = Query(None, description="Display locale, zh-CN or en-US"), db: AsyncSession = Depends(get_db), ): normalized_region = region.strip().lower() if isinstance(region, str) and region.strip() else None if normalized_region is not None and normalized_region not in REGION_ANCHORS: raise HTTPException( status_code=422, detail={ "message": "Unsupported news region.", "invalid_region": normalized_region, "allowed_regions": list(REGION_ANCHORS.keys()), }, ) return await get_earth_news_payload( lat=lat, lon=lon, region=normalized_region, categories=_parse_categories(categories), source_ids=_parse_source_ids(sources), limit=_parse_limit(limit), locale=_parse_locale(locale), db=db, )