fixed post token invalidation via mail/telegramm

This commit is contained in:
2026-02-20 15:24:17 +01:00
parent 10b07d89ac
commit c956562722
3 changed files with 61 additions and 6 deletions

View File

@@ -1613,6 +1613,12 @@ class DatabaseClient:
lambda: self.client.table("email_action_tokens").update({"used": True}).eq("token", token).execute() lambda: self.client.table("email_action_tokens").update({"used": True}).eq("token", token).execute()
) )
async def mark_all_post_tokens_used(self, post_id: UUID) -> None:
"""Mark all email action tokens for a post as used (invalidate approve + reject together)."""
await asyncio.to_thread(
lambda: self.client.table("email_action_tokens").update({"used": True}).eq("post_id", str(post_id)).execute()
)
async def cleanup_expired_email_tokens(self) -> None: async def cleanup_expired_email_tokens(self) -> None:
"""Delete expired email tokens from the database.""" """Delete expired email tokens from the database."""
now = datetime.now(timezone.utc).isoformat() now = datetime.now(timezone.utc).isoformat()

View File

@@ -336,6 +336,13 @@ class TelegramService:
async def _handle_callback_query(self, chat_id: str, cb_id: str, data: str, message_id: int, db) -> None: async def _handle_callback_query(self, chat_id: str, cb_id: str, data: str, message_id: int, db) -> None:
"""Handle inline keyboard button presses.""" """Handle inline keyboard button presses."""
# approve/reject callbacks are token-authenticated — no account lookup needed
if data.startswith("approve:") or data.startswith("reject:"):
action, token = data.split(":", 1)
await self._handle_approval_callback(chat_id, message_id, action, token, db)
return
# All other callbacks require a linked account
tg_account = await db.get_telegram_account_by_chat_id(chat_id) tg_account = await db.get_telegram_account_by_chat_id(chat_id)
if not tg_account: if not tg_account:
return return
@@ -356,6 +363,49 @@ class TelegramService:
await self._clear_conv(chat_id) await self._clear_conv(chat_id)
await self.send_message(chat_id, "🔄 Alles klar! Schreib mir dein neues Thema.") await self.send_message(chat_id, "🔄 Alles klar! Schreib mir dein neues Thema.")
async def _handle_approval_callback(
self, chat_id: str, message_id: int, action: str, token: str, db
) -> None:
"""Handle approve/reject callback from inline keyboard buttons."""
from src.services.email_service import validate_token
from uuid import UUID
token_data = await validate_token(token)
if not token_data:
await self.edit_message(
chat_id, message_id,
"❌ Dieser Link ist ungültig oder wurde bereits verwendet."
)
return
post_id = UUID(token_data["post_id"])
post = await db.get_generated_post(post_id)
if not post:
await self.edit_message(chat_id, message_id, "❌ Post nicht gefunden.")
return
if action == "approve":
new_status = "ready"
result_text = (
f"✅ <b>Post freigegeben!</b>\n\n"
f"<i>{post.topic_title or 'Post'}</i>\n\n"
f"Der Post kann jetzt eingeplant werden."
)
else:
new_status = "draft"
result_text = (
f"❌ <b>Post abgelehnt.</b>\n\n"
f"<i>{post.topic_title or 'Post'}</i>\n\n"
f"Der Post wurde zurück in die Entwürfe verschoben."
)
await db.update_generated_post(post_id, {"status": new_status})
# Invalidate both approve + reject tokens for this post
await db.mark_all_post_tokens_used(post_id)
# Edit the original message to show the result (removes buttons)
await self.edit_message(chat_id, message_id, result_text)
async def _handle_post_type_selected( async def _handle_post_type_selected(
self, chat_id: str, user_id: str, post_type_id_str: str, conv: dict, message_id: int, db self, chat_id: str, user_id: str, post_type_id_str: str, conv: dict, message_id: int, db
) -> None: ) -> None:

View File

@@ -2328,13 +2328,12 @@ async def update_post_status(
chat_id=telegram_account.telegram_chat_id, chat_id=telegram_account.telegram_chat_id,
text=( text=(
f"📋 <b>{company_name}</b> hat deinen Post bearbeitet und bittet um deine Freigabe:\n\n" f"📋 <b>{company_name}</b> hat deinen Post bearbeitet und bittet um deine Freigabe:\n\n"
f"<i>{post.topic_title or 'Untitled Post'}</i>\n\n" f"<i>{post.topic_title or 'Untitled Post'}</i>"
f"Bitte gib den Post frei oder lehne ihn ab:"
), ),
reply_markup={ reply_markup={
"inline_keyboard": [[ "inline_keyboard": [[
{"text": "✅ Freigeben", "url": approve_url}, {"text": "✅ Freigeben", "callback_data": f"approve:{approve_token}"},
{"text": "❌ Ablehnen", "url": reject_url} {"text": "❌ Ablehnen", "callback_data": f"reject:{reject_token}"}
]] ]]
} }
) )
@@ -2453,8 +2452,8 @@ async def handle_email_action(request: Request, token: str):
# Update post status # Update post status
await db.update_generated_post(post_id, {"status": new_status}) await db.update_generated_post(post_id, {"status": new_status})
# Mark token as used # Invalidate ALL tokens for this post (both approve + reject)
await mark_token_used(token) await db.mark_all_post_tokens_used(post_id)
# Send notification to creator # Send notification to creator
if profile and profile.creator_email: if profile and profile.creator_email: