93 lines
3.2 KiB
Python
93 lines
3.2 KiB
Python
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,
|
|
)
|