OCRArena / storage.py
Wassymk's picture
app
c63c0e5
"""Supabase storage helper for uploading files.
This module provides functions to upload files to Supabase storage
using HTTP requests, avoiding the dependency issues with the supabase client library.
"""
import os
import requests
import logging
from typing import Optional, Dict, Any
from pathlib import Path
# Import Supabase credentials from environment variables
from dotenv import load_dotenv
load_dotenv()
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_KEY = os.getenv("SUPABASE_KEY")
if not SUPABASE_URL or not SUPABASE_KEY:
raise ImportError("Could not load Supabase credentials from environment variables")
logger = logging.getLogger(__name__)
# Supabase storage API configuration
STORAGE_BASE_URL = f"{SUPABASE_URL}/storage/v1"
HEADERS = {
"apikey": SUPABASE_KEY,
"Authorization": f"Bearer {SUPABASE_KEY}",
"Content-Type": "application/json"
}
def upload_file_to_bucket(
file_path: str,
bucket_name: str = "images",
storage_path: Optional[str] = None,
file_options: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
Upload a file to Supabase storage bucket.
Args:
file_path: Local path to the file to upload
bucket_name: Name of the storage bucket (default: "images")
storage_path: Path in the bucket where to store the file (default: filename)
file_options: Optional file options like cache-control, upsert, etc.
Returns:
Dictionary with upload result information
"""
try:
# Check if file exists
if not os.path.exists(file_path):
raise FileNotFoundError(f"File not found: {file_path}")
# Get file info
file_path_obj = Path(file_path)
file_name = file_path_obj.name
# Use provided storage_path or default to filename
if storage_path is None:
storage_path = file_name
# Prepare upload URL
upload_url = f"{STORAGE_BASE_URL}/object/{bucket_name}/{storage_path}"
# Prepare headers for file upload
upload_headers = {
"apikey": SUPABASE_KEY,
"Authorization": f"Bearer {SUPABASE_KEY}"
}
# Add file options if provided
if file_options:
for key, value in file_options.items():
upload_headers[f"x-upsert"] = str(value).lower() if key == "upsert" else str(value)
# Read and upload file
with open(file_path, "rb") as f:
response = requests.post(
upload_url,
headers=upload_headers,
data=f
)
if response.status_code == 200:
result = response.json()
logger.info(f"✅ File uploaded successfully: {file_name}")
logger.info(f"📁 Storage path: {storage_path}")
logger.info(f"🔗 Public URL: {result.get('publicUrl', 'N/A')}")
return {
"success": True,
"file_name": file_name,
"storage_path": storage_path,
"public_url": result.get('publicUrl'),
"response": result
}
else:
logger.error(f"❌ Upload failed with status {response.status_code}")
logger.error(f"Response: {response.text}")
return {
"success": False,
"error": f"Upload failed: {response.status_code}",
"response": response.text
}
except Exception as e:
logger.error(f"❌ Error uploading file: {e}")
return {
"success": False,
"error": str(e)
}
def get_file_url(bucket_name: str, file_path: str) -> str:
"""
Get the public URL for a file in Supabase storage.
Args:
bucket_name: Name of the storage bucket
file_path: Path to the file in the bucket
Returns:
Public URL for the file
"""
return f"{SUPABASE_URL}/storage/v1/object/public/{bucket_name}/{file_path}"
def list_bucket_files(bucket_name: str = "images") -> Dict[str, Any]:
"""
List all files in a storage bucket.
Args:
bucket_name: Name of the storage bucket
Returns:
Dictionary with list of files
"""
try:
list_url = f"{STORAGE_BASE_URL}/object/list/{bucket_name}"
response = requests.get(list_url, headers=HEADERS)
if response.status_code == 200:
result = response.json()
logger.info(f"✅ Listed {len(result)} files in bucket '{bucket_name}'")
return {
"success": True,
"files": result
}
else:
logger.error(f"❌ Failed to list files: {response.status_code}")
return {
"success": False,
"error": f"Failed to list files: {response.status_code}"
}
except Exception as e:
logger.error(f"❌ Error listing files: {e}")
return {
"success": False,
"error": str(e)
}
def test_upload_image_png():
"""Test function to upload Image.png to the images bucket."""
try:
logger.info("🚀 Testing file upload to Supabase storage...")
# First, test if we can list files (this tests basic connectivity)
logger.info("📋 Testing bucket connectivity...")
list_result = list_bucket_files("images")
if list_result["success"]:
logger.info(f"✅ Bucket connectivity successful! Found {len(list_result['files'])} files")
else:
logger.warning(f"⚠️ Bucket connectivity issue: {list_result['error']}")
# Test upload
logger.info("📤 Testing file upload...")
result = upload_file_to_bucket(
file_path="Image.png",
bucket_name="images",
storage_path="test/Image.png",
file_options={"cache-control": "3600", "upsert": "false"}
)
if result["success"]:
logger.info("✅ Upload test successful!")
logger.info(f"📁 File uploaded to: {result['storage_path']}")
logger.info(f"🔗 Public URL: {result['public_url']}")
return True
else:
logger.error(f"❌ Upload test failed: {result['error']}")
# Provide helpful error information
if "row-level security policy" in result.get('response', ''):
logger.error("🔧 This is a storage permissions issue.")
logger.error("📋 To fix this, you need to configure your Supabase storage bucket:")
logger.error("""
1. Go to your Supabase dashboard
2. Navigate to Storage > Policies
3. For the 'images' bucket, add a policy like:
CREATE POLICY "Allow public uploads" ON storage.objects
FOR INSERT WITH CHECK (bucket_id = 'images');
CREATE POLICY "Allow public reads" ON storage.objects
FOR SELECT USING (bucket_id = 'images');
Or make the bucket public in Storage > Settings
""")
return False
except Exception as e:
logger.error(f"❌ Upload test error: {e}")
return False