asim san
asim san

Reputation: 21

uploading media files in supabase storage bucket with clerk authentication

I'm trying to upload images to Supabase storage from a React Native Expo app. The images are first compressed using expo-image-manipulator, but the upload fails with:

ERROR Error uploading images: No content provided ERROR Upload Media Error: {"error": "InvalidRequest", "message": "No content provided", "statusCode": "400"}

I am also not sure how to properly configure the RLS for Supabase storage bucket as I am using authentication from clerk.

import React, { useEffect, useState } from 'react';
import {
  View,
  Text,
  StyleSheet,
  Image,
  TouchableOpacity,
  ScrollView,
  Alert,
  ActivityIndicator,
} from 'react-native';
import { useRouter, useLocalSearchParams } from 'expo-router';
import * as Location from 'expo-location';
import { createClient, SupabaseClient } from '@supabase/supabase-js';
import * as ImageManipulator from 'expo-image-manipulator';
import { v4 as uuidv4 } from 'uuid';
import { useAuth, useUser } from '@clerk/clerk-expo';
import { supabaseUrl, supabaseAnonKey } from '@/config/supabaseClient';
import { jwtDecode } from 'jwt-decode'; // Corrected import

// Define the uploadMedia function
const uploadMedia = async (
  uri: string,
  mediaType: 'images' | 'videos',
  supabaseClient: SupabaseClient
): Promise<string | null> => {
  try {
    const extension = uri.split('.').pop();
    const filename = `${uuidv4()}.${extension}`;
    const path = `${mediaType}/${filename}`;

    // For local files on device, we need to prepend 'file://'
    const formattedUri = uri.startsWith('file://') ? uri : `file://${uri}`;
    
    const response = await fetch(formattedUri);
    if (!response.ok) throw new Error(`Failed to fetch media: ${response.status}`);
    
    const blob = await response.blob();
    if (blob.size === 0) throw new Error('Blob is empty');

    console.log('Uploading blob:', { size: blob.size, type: blob.type });

    const { error } = await supabaseClient.storage
      .from('food-listings-media')
      .upload(path, blob);

    if (error) throw error;

    const { data: { publicUrl } } = supabaseClient.storage
      .from('food-listings-media')
      .getPublicUrl(path);

    if (!publicUrl) throw new Error('Failed to get public URL');

    return publicUrl;
  } catch (error) {
    console.error('Upload Media Error:', error);
    return null;
  }
};

export default function Summary() {
  const router = useRouter();
  const { newListing } = useLocalSearchParams();
  const listing = typeof newListing === 'string' ? JSON.parse(newListing) : newListing;

  const [address, setAddress] = useState<string | null>(null);
  const [errorMsg, setErrorMsg] = useState<string | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const { getToken } = useAuth();
  const { user } = useUser(); // Get the user from Clerk

  

  useEffect(() => {
   

  // Create the Supabase client with Clerk token
  const createClerkSupabaseClient = () => {
    return createClient(supabaseUrl, supabaseAnonKey, {
      global: {
        fetch: async (url, options = {}) => {
          const token = await getToken({ template: 'supabase' });
          if (token) {
            const decodedToken = jwtDecode(token);
            console.log('User Subject (sub) from JWT:', decodedToken.sub); // Log the subject status
          } else {
            console.log('No token found for the user.');
          }

          const headers = new Headers(options?.headers);
          headers.set('Authorization', `Bearer ${token}`);

          return fetch(url, {
            ...options,
            headers,
          });
        },
      },
    });
  };

  const handleSubmit = async () => {
    try {
      setLoading(true);
      const supabaseClient = createClerkSupabaseClient();

      // Upload images
      const uploadedImages = await Promise.all(
        images.map(async (imageUri: string) => {
          const compressedImage = await ImageManipulator.manipulateAsync(
            imageUri,
            [{ resize: { width: 800 } }],
            { compress: 0.7, format: ImageManipulator.SaveFormat.JPEG }
          );
          const imageUrl = await uploadMedia(compressedImage.uri, 'images', supabaseClient);
          if (!imageUrl) throw new Error('Image upload failed');
          return imageUrl;
        })
      );

      // Upload videos
      const uploadedVideos = await Promise.all(
        videos.map(async (videoUri: string) => {
          const videoUrl = await uploadMedia(videoUri, 'videos', supabaseClient);
          if (!videoUrl) throw new Error('Video upload failed');
          return videoUrl;
        })
      );

      // Prepare listing data with uploaded media URLs
      const listingData = {
        user_id: user?.id, // Use the original Clerk user ID here
        title,
        description,
        cuisine_type: selectedCuisine,
        is_vegan: false, // Set based on your app's logic
        is_vegetarian: false, // Set based on your app's logic
        max_guests: parseInt(maxGuests, 10),
        price_per_meal: parseFloat(pricePerMeal),
        discount_for_groups: null, // Set as needed
        location: location
          ? { latitude: location.latitude, longitude: location.longitude }
          : null,
        rules,
        availability,
        images: uploadedImages,
        videos: uploadedVideos,
        gifs: null, // Set as needed
      };

      console.log('Listing Data to Insert:', listingData);

      // Insert the data into the listings table
      const { error: insertError } = await supabaseClient
        .from('listings')
        .insert([listingData]);

      if (insertError) {
        console.error('Error inserting data:', insertError);
        Alert.alert('Error', 'Failed to submit the listing. Please try again.');
      } else {
        Alert.alert('Success', 'Your listing has been added successfully!');
        router.push({ pathname: './(tabs)/index' }); // Updated to use correct path
      }
    } catch (error: unknown) {
      const errorMessage =
        error instanceof Error
          ? error.message
          : 'An unexpected error occurred. Please try again.';
      Alert.alert('Error', errorMessage);
      console.error('Submission error:', error);
    } finally {
      setLoading(false);
    }
  };

  

Upvotes: 2

Views: 193

Answers (0)

Related Questions