import os import shutil from uuid import uuid4 from fastapi import UploadFile from typing import Optional from datetime import datetime from sqlmodel import Session, select from app.models.coupon import Coupon # Use absolute path relative to THIS file's location (app/services/image_handler.py) # Going up 3 levels: services -> app -> backend -> uploads _BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) UPLOAD_BASE = os.path.join(_BASE_DIR, "uploads") def save_image(image: UploadFile, subfolder: str) -> str: """Saves an image to the local filesystem and returns its relative URL.""" upload_dir = os.path.join(UPLOAD_BASE, subfolder) os.makedirs(upload_dir, exist_ok=True) # Generate unique filename ext = os.path.splitext(image.filename or "")[1].lower() if not ext or ext not in ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.avif']: ext = ".jpg" # Default extension if missing or invalid filename = f"{uuid4()}{ext}" path = os.path.join(upload_dir, filename) # Reset file pointer to start before reading image.file.seek(0) # Save file with open(path, "wb") as buffer: shutil.copyfileobj(image.file, buffer) print(f"DEBUG: Saved image to {path}") return f"/uploads/{subfolder}/{filename}" def delete_image(image_url: Optional[str]): """Deletes an image from the filesystem if it's stored locally.""" if not image_url: return # We only delete if it points to our local uploads folder if image_url.startswith("/uploads/"): # Build absolute path relative = image_url.lstrip("/") # e.g. "uploads/businesses/uuid.jpg" file_path = os.path.join(_BASE_DIR, relative) if os.path.exists(file_path): try: os.remove(file_path) print(f"DEBUG: Deleted file {file_path}") except Exception as e: print(f"ERROR: Failed to delete {file_path}: {e}") else: print(f"DEBUG: File not found for deletion: {file_path}") def cleanup_expired_coupons(session: Session): """Finds and deletes expired coupons and their associated images.""" try: now = datetime.now() # Coupons where valid_until is passed statement = select(Coupon).where(Coupon.valid_until < now) expired = session.exec(statement).all() count = 0 for coupon in expired: if coupon.image_url: delete_image(coupon.image_url) session.delete(coupon) count += 1 if count > 0: session.commit() print(f"DEBUG: Cleaned up {count} expired coupons and their images.") except Exception as e: print(f"ERROR: Cleanup failed: {e}")