fc3e: /api/posts blueprint — list (with cursor + filters) + detail
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
"""FC-3e: /api/posts — cursor-paginated unified posts feed."""
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from ..extensions import get_session
|
||||
from ..services.post_feed_service import PostFeedService
|
||||
from ..services.source_service import KNOWN_PLATFORMS
|
||||
|
||||
posts_bp = Blueprint("posts", __name__, url_prefix="/api/posts")
|
||||
|
||||
|
||||
def _bad(error: str, *, status: int = 400, detail: str | None = None, **extra):
|
||||
body = {"error": error}
|
||||
if detail is not None:
|
||||
body["detail"] = detail
|
||||
body.update(extra)
|
||||
return jsonify(body), status
|
||||
|
||||
|
||||
@posts_bp.route("", methods=["GET"])
|
||||
async def list_posts():
|
||||
args = request.args
|
||||
|
||||
cursor = args.get("cursor") or None
|
||||
artist_id_raw = args.get("artist_id")
|
||||
platform = args.get("platform") or None
|
||||
limit_raw = args.get("limit", "24")
|
||||
|
||||
try:
|
||||
limit = int(limit_raw)
|
||||
except ValueError:
|
||||
return _bad("invalid_limit", detail="limit must be an integer")
|
||||
if limit < 1 or limit > 100:
|
||||
return _bad("invalid_limit", detail="limit must be between 1 and 100")
|
||||
|
||||
artist_id = None
|
||||
if artist_id_raw is not None:
|
||||
try:
|
||||
artist_id = int(artist_id_raw)
|
||||
except ValueError:
|
||||
return _bad("invalid_artist_id", detail="artist_id must be an integer")
|
||||
|
||||
if platform is not None and platform not in KNOWN_PLATFORMS:
|
||||
return _bad(
|
||||
"unknown_platform",
|
||||
detail=f"platform must be one of {sorted(KNOWN_PLATFORMS)}",
|
||||
)
|
||||
|
||||
async with get_session() as session:
|
||||
try:
|
||||
page = await PostFeedService(session).scroll(
|
||||
cursor=cursor, artist_id=artist_id,
|
||||
platform=platform, limit=limit,
|
||||
)
|
||||
except ValueError as exc:
|
||||
# Service raises ValueError for malformed cursors only;
|
||||
# limit bounds are validated above.
|
||||
return _bad("invalid_cursor", detail=str(exc))
|
||||
|
||||
return jsonify(page)
|
||||
|
||||
|
||||
@posts_bp.route("/<int:post_id>", methods=["GET"])
|
||||
async def get_post(post_id: int):
|
||||
async with get_session() as session:
|
||||
item = await PostFeedService(session).get_post(post_id)
|
||||
if item is None:
|
||||
return _bad("not_found", status=404, detail=f"post id={post_id}")
|
||||
return jsonify(item)
|
||||
Reference in New Issue
Block a user