diff --git a/fotograf-de-scraper/backend/publish_request_api.py b/fotograf-de-scraper/backend/publish_request_api.py index 956fb7eb9..e9782d7ce 100644 --- a/fotograf-de-scraper/backend/publish_request_api.py +++ b/fotograf-de-scraper/backend/publish_request_api.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException, Request +from fastapi import APIRouter, Depends, HTTPException, Request, BackgroundTasks from pydantic import BaseModel from sqlalchemy.orm import Session from database import get_db, DiscountCode @@ -6,6 +6,9 @@ import datetime import logging from gmail_service import GmailService import re +import time +import asyncio +from typing import List, Dict, Optional router = APIRouter(prefix="/api/publish-request", tags=["publish-request"]) logger = logging.getLogger("publish-request") @@ -13,6 +16,54 @@ logger = logging.getLogger("publish-request") class CodesUpload(BaseModel): codes: str # comma separated +class SendReleaseRequest(BaseModel): + emails: List[Dict[str, str]] + scheduled_time: Optional[str] = None # e.g. "10:00" + +async def delayed_send(emails: List[Dict[str, str]], scheduled_time: str, db: Session): + try: + # Calculate delay + now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=2))) # Berlin Time Approx + target_h, target_m = map(int, scheduled_time.split(":")) + target_time = now.replace(hour=target_h, minute=target_m, second=0, microsecond=0) + + if target_time < now: + target_time += datetime.timedelta(days=1) + + delay_seconds = (target_time - now).total_seconds() + logger.info(f"Scheduling {len(emails)} emails for {scheduled_time} (in {delay_seconds} seconds)") + + await asyncio.sleep(delay_seconds) + + service = GmailService(db) + success_count = 0 + for email_data in emails: + if service.send_email(email_data["to"], email_data["subject"], email_data["body"]): + success_count += 1 + await asyncio.sleep(1) # Rate limiting + + logger.info(f"Scheduled send complete: {success_count}/{len(emails)} success.") + except Exception as e: + logger.exception("Error in delayed_send background task") + +@router.post("/send") +async def send_requests(data: SendReleaseRequest, background_tasks: BackgroundTasks, db: Session = Depends(get_db)): + if data.scheduled_time: + background_tasks.add_task(delayed_send, data.emails, data.scheduled_time, db) + return {"status": "scheduled", "message": f"Versand für {data.scheduled_time} geplant."} + + # Immediate send + service = GmailService(db) + success = 0 + failed = [] + for email_data in data.emails: + if service.send_email(email_data["to"], email_data["subject"], email_data["body"]): + success += 1 + else: + failed.append(email_data["to"]) + + return {"status": "success", "success": success, "failed": failed} + @router.get("/stats") def get_stats(db: Session = Depends(get_db)): total = db.query(DiscountCode).count() diff --git a/fotograf-de-scraper/frontend/src/App.tsx b/fotograf-de-scraper/frontend/src/App.tsx index 51330bb7b..786d2bb81 100644 --- a/fotograf-de-scraper/frontend/src/App.tsx +++ b/fotograf-de-scraper/frontend/src/App.tsx @@ -61,6 +61,7 @@ function App() { const [uploadMessage, setUploadMessage] = useState(""); const [isSendingRelease, setIsSendingRelease] = useState(false); const [releaseMessage, setReleaseMessage] = useState(""); + const [scheduledTime, setScheduledTime] = useState(""); // New state const fetchReleaseStats = async () => { try { @@ -136,10 +137,13 @@ function App() { setReleaseMessage(`Sende ${emailsToSend.length} Mails...`); try { - const response = await fetch(`${API_BASE_URL}/api/gmail/send-bulk`, { + const response = await fetch(`${API_BASE_URL}/api/publish-request/send`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ emails: emailsToSend }) + body: JSON.stringify({ + emails: emailsToSend, + scheduled_time: scheduledTime || null + }) }); const data = await response.json(); if (response.ok) { @@ -553,10 +557,13 @@ function App() { }); try { - const response = await fetch(`${API_BASE_URL}/api/gmail/send-bulk`, { + const response = await fetch(`${API_BASE_URL}/api/publish-request/send`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ emails: emailsToSend }) + body: JSON.stringify({ + emails: emailsToSend, + scheduled_time: scheduledTime || null + }) }); if (response.ok) { @@ -1021,6 +1028,17 @@ function App() { onChange={(e) => setReleaseEmails(e.target.value)} className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 mb-2 h-20" /> + +