from database import get_db, DiscountCode, ReleaseParticipant router = APIRouter(prefix="/api/publish-request", tags=["publish-request"]) logger = logging.getLogger("publish-request") # Official Project Signature SIGNATURE_HTML = """

--
Kinderfotos Erding Logo

Kinderfotos Erding | www.kinderfotos-erding.de

Gartenstr. 10 | 85445 Oberding | 08122-8470867

""" class CodesUpload(BaseModel): codes: str # comma separated class SendReleaseRequest(BaseModel): emails: List[Dict[str, str]] scheduled_time: Optional[str] = None # e.g. "10:00" participants: Optional[List[Dict[str, str]]] = None # [{email, first_name}] async def delayed_send(emails: List[Dict[str, str]], scheduled_time: str, db_session_factory): 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) # We need a fresh DB session for the background task db = db_session_factory() try: 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.") finally: db.close() 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)): # Store participant names for later (webhook) if data.participants: for p in data.participants: email = p.get("email", "").strip().lower() first_name = p.get("first_name", "").strip() if email and first_name: existing = db.query(ReleaseParticipant).filter(ReleaseParticipant.email == email).first() if existing: existing.first_name = first_name else: db.add(ReleaseParticipant(email=email, first_name=first_name)) db.commit() if data.scheduled_time: # Pass a way to get a new session to the background task from database import SessionLocal background_tasks.add_task(delayed_send, data.emails, data.scheduled_time, SessionLocal) 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() used = db.query(DiscountCode).filter(DiscountCode.is_used == 1).count() available = total - used return {"total": total, "used": used, "available": available} @router.post("/codes") def upload_codes(data: CodesUpload, db: Session = Depends(get_db)): codes_list = [c.strip() for c in data.codes.split(",") if c.strip()] added = 0 for code in set(codes_list): existing = db.query(DiscountCode).filter(DiscountCode.code == code).first() if not existing: new_code = DiscountCode(code=code, is_used=0) db.add(new_code) added += 1 db.commit() return {"status": "success", "added": added} class WebhookData(BaseModel): email: str @router.post("/webhook") async def handle_webhook(request: Request, db: Session = Depends(get_db)): # Try to parse JSON from Google Forms webhook try: data = await request.json() except: raise HTTPException(status_code=400, detail="Invalid JSON") # We expect {"email": "..."} or similar from the Google Apps Script email = data.get("email") or data.get("Email") if not email: logger.error(f"Webhook received without email: {data}") return {"status": "error", "message": "Email not found in webhook payload"} email = email.strip().lower() # Check if this email already got a code already_assigned = db.query(DiscountCode).filter(DiscountCode.assigned_to_email == email).first() if already_assigned: logger.info(f"Email {email} already received code {already_assigned.code}") return {"status": "success", "message": "Already sent"} # Get a free code free_code = db.query(DiscountCode).filter(DiscountCode.is_used == 0).first() if not free_code: logger.error("NO FREE DISCOUNT CODES LEFT!") return {"status": "error", "message": "No codes available"} # Look up participant name participant = db.query(ReleaseParticipant).filter(ReleaseParticipant.email == email).first() first_name = participant.first_name if participant else "Ihr Lieben" # Mark as used free_code.is_used = 1 free_code.assigned_to_email = email free_code.used_at = datetime.datetime.utcnow() db.commit() # Send Thank You Email with GmailService service = GmailService(db) subject = "Dankeschön für Eure Freigabe & Euer Rabattcode" # Image provided by user INSTRUCTIONS_IMAGE_URL = "https://mail.google.com/mail/u/2?ui=2&ik=719adaa3c5&attid=0.1&permmsgid=msg-a:r7482671925923393616&th=196e322c399dbc7f&view=fimg&fur=ip&permmsgid=msg-a:r7482671925923393616&sz=s0-l75-ft&attbid=ANGjdJ9_U6ayMFgwbupt4HalTKO867IHx6N70eNbPfQmTLNzRXilJxI-n8a1gjM8xVcP5HEOgaVxfp3FnJPzTYEEYhK4gSU-Il_0a6OtzFYscp55_W4iyxuxjyPvK4&disp=emb&realattid=ii_maspzxv50&zw" body_html = f"""

Hallo {first_name},

Vielen Dank nochmal für die Freigabe zur Veröffentlichung, das ist super nett von Euch!

Hier ist euer Gutscheincode über 25 Euro: {free_code.code}

Um den Gutschein einzugeben, musst du auf den Preis des Warenkorbs drücken (über dem Button zur Kasse gehen):

Anleitung Gutschein einlösen

Liebe Grüße,
das Team von Kinderfotos Erding

{SIGNATURE_HTML} """ try: success = service.send_email(email, subject, body_html) if success: logger.info(f"Successfully sent code {free_code.code} to {email}") return {"status": "success", "message": "Email sent"} else: logger.error(f"Failed to send email to {email}") free_code.is_used = 0 free_code.assigned_to_email = None free_code.used_at = None db.commit() return {"status": "error", "message": "Failed to send email"} except Exception as e: logger.exception("Error sending webhook email") free_code.is_used = 0 free_code.assigned_to_email = None free_code.used_at = None db.commit() return {"status": "error", "message": str(e)}