import { serve } from "https://deno.land/std@0.168.0/http/server.ts" const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', } serve(async (req) => { // Handle CORS preflight requests if (req.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }) } try { const CLOUDFLARE_ACCOUNT_ID = Deno.env.get('CLOUDFLARE_ACCOUNT_ID') const CLOUDFLARE_IMAGES_API_TOKEN = Deno.env.get('CLOUDFLARE_IMAGES_API_TOKEN') if (!CLOUDFLARE_ACCOUNT_ID || !CLOUDFLARE_IMAGES_API_TOKEN) { throw new Error('Missing Cloudflare credentials') } if (req.method !== 'POST') { return new Response( JSON.stringify({ error: 'Method not allowed' }), { status: 405, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } const formData = await req.formData() const file = formData.get('file') as File if (!file) { return new Response( JSON.stringify({ error: 'No file provided' }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } // Validate file size (10MB limit) const maxSize = 10 * 1024 * 1024 // 10MB if (file.size > maxSize) { return new Response( JSON.stringify({ error: 'File size exceeds 10MB limit' }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } // Validate file type const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'] if (!allowedTypes.includes(file.type)) { return new Response( JSON.stringify({ error: 'Invalid file type. Only JPEG, PNG, and WebP are allowed.' }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } // Create FormData for Cloudflare Images API const cloudflareFormData = new FormData() cloudflareFormData.append('file', file) // Optional metadata const metadata = formData.get('metadata') if (metadata) { cloudflareFormData.append('metadata', metadata.toString()) } // Upload to Cloudflare Images const uploadResponse = await fetch( `https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/images/v1`, { method: 'POST', headers: { 'Authorization': `Bearer ${CLOUDFLARE_IMAGES_API_TOKEN}`, }, body: cloudflareFormData, } ) const uploadResult = await uploadResponse.json() if (!uploadResponse.ok) { console.error('Cloudflare upload error:', uploadResult) return new Response( JSON.stringify({ error: 'Failed to upload image', details: uploadResult.errors || uploadResult.error }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } // Return the upload result with image URLs return new Response( JSON.stringify({ success: true, id: uploadResult.result.id, filename: uploadResult.result.filename, uploaded: uploadResult.result.uploaded, variants: uploadResult.result.variants, // Provide convenient URLs for different sizes urls: { original: uploadResult.result.variants[0], // First variant is usually the original thumbnail: `${uploadResult.result.variants[0]}/w=400,h=400,fit=crop`, // 400x400 thumbnail medium: `${uploadResult.result.variants[0]}/w=800,h=600,fit=cover`, // 800x600 medium large: `${uploadResult.result.variants[0]}/w=1200,h=900,fit=cover`, // 1200x900 large } }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } catch (error) { console.error('Upload error:', error) return new Response( JSON.stringify({ error: 'Internal server error', message: error.message }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } })