File size: 7,443 Bytes
c63c0e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
"""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