From c9565627225e5105f2f912c8a82d18e41d5cad47 Mon Sep 17 00:00:00 2001 From: Ruben Fischer Date: Fri, 20 Feb 2026 15:24:17 +0100 Subject: [PATCH] fixed post token invalidation via mail/telegramm --- src/database/client.py | 6 ++++ src/services/telegram_service.py | 50 ++++++++++++++++++++++++++++++++ src/web/user/routes.py | 11 ++++--- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/database/client.py b/src/database/client.py index 2a4b5f9..44181d8 100644 --- a/src/database/client.py +++ b/src/database/client.py @@ -1613,6 +1613,12 @@ class DatabaseClient: 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: """Delete expired email tokens from the database.""" now = datetime.now(timezone.utc).isoformat() diff --git a/src/services/telegram_service.py b/src/services/telegram_service.py index 5d98dd6..0b084f3 100644 --- a/src/services/telegram_service.py +++ b/src/services/telegram_service.py @@ -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: """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) if not tg_account: return @@ -356,6 +363,49 @@ class TelegramService: await self._clear_conv(chat_id) 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"✅ Post freigegeben!\n\n" + f"{post.topic_title or 'Post'}\n\n" + f"Der Post kann jetzt eingeplant werden." + ) + else: + new_status = "draft" + result_text = ( + f"❌ Post abgelehnt.\n\n" + f"{post.topic_title or 'Post'}\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( self, chat_id: str, user_id: str, post_type_id_str: str, conv: dict, message_id: int, db ) -> None: diff --git a/src/web/user/routes.py b/src/web/user/routes.py index f5fd7b7..2f2d479 100644 --- a/src/web/user/routes.py +++ b/src/web/user/routes.py @@ -2328,13 +2328,12 @@ async def update_post_status( chat_id=telegram_account.telegram_chat_id, text=( f"📋 {company_name} hat deinen Post bearbeitet und bittet um deine Freigabe:\n\n" - f"{post.topic_title or 'Untitled Post'}\n\n" - f"Bitte gib den Post frei oder lehne ihn ab:" + f"{post.topic_title or 'Untitled Post'}" ), reply_markup={ "inline_keyboard": [[ - {"text": "✅ Freigeben", "url": approve_url}, - {"text": "❌ Ablehnen", "url": reject_url} + {"text": "✅ Freigeben", "callback_data": f"approve:{approve_token}"}, + {"text": "❌ Ablehnen", "callback_data": f"reject:{reject_token}"} ]] } ) @@ -2453,8 +2452,8 @@ async def handle_email_action(request: Request, token: str): # Update post status await db.update_generated_post(post_id, {"status": new_status}) - # Mark token as used - await mark_token_used(token) + # Invalidate ALL tokens for this post (both approve + reject) + await db.mark_all_post_tokens_used(post_id) # Send notification to creator if profile and profile.creator_email: