feat(briefing): add POST/DELETE /api/briefing/rss-reactions endpoints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -235,3 +235,89 @@ async def manual_trigger():
|
|||||||
text, metadata = await run_compilation(g.user.id, slot, model)
|
text, metadata = await run_compilation(g.user.id, slot, model)
|
||||||
msg = await post_message(conv.id, "assistant", text, metadata=metadata)
|
msg = await post_message(conv.id, "assistant", text, metadata=metadata)
|
||||||
return jsonify({"conversation_id": conv.id, "message_id": msg.id, "slot": slot})
|
return jsonify({"conversation_id": conv.id, "message_id": msg.id, "slot": slot})
|
||||||
|
|
||||||
|
|
||||||
|
# ── RSS Reactions ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@briefing_bp.route("/rss-reactions", methods=["POST"])
|
||||||
|
@_REQUIRE
|
||||||
|
async def upsert_rss_reaction():
|
||||||
|
"""Upsert a 👍/👎 reaction on an RSS item. Same reaction toggles off; opposite flips."""
|
||||||
|
data = await request.get_json()
|
||||||
|
rss_item_id = data.get("rss_item_id")
|
||||||
|
reaction = data.get("reaction")
|
||||||
|
|
||||||
|
if not rss_item_id or reaction not in ("up", "down"):
|
||||||
|
return jsonify({"error": "rss_item_id and reaction ('up'|'down') required"}), 400
|
||||||
|
|
||||||
|
from sqlalchemy import text as _text
|
||||||
|
|
||||||
|
async with async_session() as session:
|
||||||
|
# Ownership check: verify item belongs to a feed owned by this user
|
||||||
|
result = await session.execute(
|
||||||
|
_text("""
|
||||||
|
SELECT i.id FROM rss_items i
|
||||||
|
JOIN rss_feeds f ON f.id = i.feed_id
|
||||||
|
WHERE i.id = :item_id AND f.user_id = :uid
|
||||||
|
""").bindparams(item_id=rss_item_id, uid=g.user.id)
|
||||||
|
)
|
||||||
|
if result.first() is None:
|
||||||
|
return jsonify({"error": "Not found"}), 404
|
||||||
|
|
||||||
|
# Check existing reaction
|
||||||
|
existing = await session.execute(
|
||||||
|
_text("""
|
||||||
|
SELECT id, reaction FROM rss_item_reactions
|
||||||
|
WHERE user_id = :uid AND rss_item_id = :item_id
|
||||||
|
""").bindparams(uid=g.user.id, item_id=rss_item_id)
|
||||||
|
)
|
||||||
|
row = existing.first()
|
||||||
|
|
||||||
|
if row is None:
|
||||||
|
await session.execute(
|
||||||
|
_text("""
|
||||||
|
INSERT INTO rss_item_reactions (user_id, rss_item_id, reaction)
|
||||||
|
VALUES (:uid, :item_id, :reaction)
|
||||||
|
""").bindparams(uid=g.user.id, item_id=rss_item_id, reaction=reaction)
|
||||||
|
)
|
||||||
|
action = "created"
|
||||||
|
elif row.reaction == reaction:
|
||||||
|
# Toggle off (same reaction clicked again)
|
||||||
|
await session.execute(
|
||||||
|
_text("""
|
||||||
|
DELETE FROM rss_item_reactions
|
||||||
|
WHERE user_id = :uid AND rss_item_id = :item_id
|
||||||
|
""").bindparams(uid=g.user.id, item_id=rss_item_id)
|
||||||
|
)
|
||||||
|
action = "removed"
|
||||||
|
else:
|
||||||
|
# Flip to opposite reaction
|
||||||
|
await session.execute(
|
||||||
|
_text("""
|
||||||
|
UPDATE rss_item_reactions SET reaction = :reaction
|
||||||
|
WHERE user_id = :uid AND rss_item_id = :item_id
|
||||||
|
""").bindparams(reaction=reaction, uid=g.user.id, item_id=rss_item_id)
|
||||||
|
)
|
||||||
|
action = "updated"
|
||||||
|
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
return jsonify({"ok": True, "action": action})
|
||||||
|
|
||||||
|
|
||||||
|
@briefing_bp.route("/rss-reactions/<int:item_id>", methods=["DELETE"])
|
||||||
|
@_REQUIRE
|
||||||
|
async def delete_rss_reaction(item_id: int):
|
||||||
|
"""Explicitly remove a reaction (useful for MCP/external API callers)."""
|
||||||
|
from sqlalchemy import text as _text
|
||||||
|
|
||||||
|
async with async_session() as session:
|
||||||
|
await session.execute(
|
||||||
|
_text("""
|
||||||
|
DELETE FROM rss_item_reactions
|
||||||
|
WHERE user_id = :uid AND rss_item_id = :item_id
|
||||||
|
""").bindparams(uid=g.user.id, item_id=item_id)
|
||||||
|
)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
return jsonify({"ok": True})
|
||||||
|
|||||||
Reference in New Issue
Block a user