language-learning-app/api/app/routers/bff/articles.py
2026-06-02 21:02:50 +01:00

100 lines
2.7 KiB
Python

from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
from app.domain.services.article_service import ArticleService
from app.outbound.postgres.repositories.article_repository import (
PostgresArticleOwnershipRepository,
PostgresArticleRepository,
)
from ...auth import verify_token
from ...outbound.postgres.database import get_db
from ...outbound.storage_client import get_storage_client
router = APIRouter(prefix="/articles", tags=["bff", "articles"])
def _make_article_service(db) -> ArticleService:
return ArticleService(
article_repository=PostgresArticleRepository(db),
article_ownership_repository=PostgresArticleOwnershipRepository(db),
)
class ArticleItem(BaseModel):
id: str
published_at: datetime | None
language: str
title: str
complexity: str
class ArticleListResponse(BaseModel):
articles: list[ArticleItem]
class ArticleDetail(BaseModel):
id: str
published_at: datetime | None
language: str
complexity: str
title: str
body: str
audio_url: str | None
body_pos: dict | None
def _audio_url(key: str | None) -> str | None:
if key is None:
return None
return get_storage_client().get_url(key)
@router.get("", response_model=ArticleListResponse, status_code=200)
async def list_articles(
db: AsyncSession = Depends(get_db),
token_data: dict = Depends(verify_token),
) -> ArticleListResponse:
service = _make_article_service(db)
user_id = token_data["sub"]
articles = await service.get_articles_for_user(user_id=user_id)
return ArticleListResponse(
articles=[
ArticleItem(
id=a.id,
published_at=a.published_at,
language=a.language,
title=a.title,
complexity=a.target_complexity,
)
for a in articles
]
)
@router.get("/{article_id}", response_model=ArticleDetail, status_code=200)
async def get_article(
article_id: str,
db: AsyncSession = Depends(get_db),
token_data: dict = Depends(verify_token),
) -> ArticleDetail:
uid: str = token_data["sub"]
service = _make_article_service(db)
article = await service.get_article_as_user(article_id, uid)
if article is None:
raise HTTPException(status_code=404, detail="Article not found")
return ArticleDetail(
id=article.id,
published_at=article.published_at,
language=article.language,
complexity=article.target_complexity,
title=article.title,
body=article.text,
body_pos=article.text_linguistic_data,
audio_url=_audio_url(article.audio_key),
)