Browse Source

fix: add missing store-data endpoints to API #52

Claude 5 months ago
parent
commit
6d9292eb54
1 changed files with 517 additions and 0 deletions
  1. 517 0
      supabase/functions/api/index.ts

+ 517 - 0
supabase/functions/api/index.ts

@@ -987,6 +987,523 @@ serve(wrapHandler('api', async (req) => {
       }
       }
     }
     }
 
 
+    // GET /api/store-data/products|orders|customers - Get store data by type
+    if (path.match(/^store-data\/(products|orders|customers)$/) && req.method === 'GET') {
+      const dataType = path.split('/')[1] // products, orders, or customers
+      const storeId = url.searchParams.get('store_id')
+      const page = parseInt(url.searchParams.get('page') || '1')
+      const limit = parseInt(url.searchParams.get('limit') || '25')
+      const search = url.searchParams.get('search')
+      const enabledFilter = url.searchParams.get('enabled')
+
+      if (!storeId) {
+        return new Response(
+          JSON.stringify({ error: 'store_id is required' }),
+          { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      // Verify store ownership
+      const { data: store, error: storeError } = await supabase
+        .from('stores')
+        .select('id, platform_name')
+        .eq('id', storeId)
+        .eq('user_id', user.id)
+        .single()
+
+      if (storeError || !store) {
+        return new Response(
+          JSON.stringify({ error: 'Store not found or access denied' }),
+          { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      try {
+        // Map dataType to singular form for table lookup
+        const singularType = dataType === 'products' ? 'product' : dataType === 'orders' ? 'order' : 'customer'
+        const platform = store.platform_name
+
+        // Get table info based on platform
+        const tableName = platform === 'woocommerce'
+          ? `woocommerce_${dataType}_cache`
+          : platform === 'shopify'
+          ? `shopify_${dataType}_cache`
+          : `shoprenter_${dataType}_cache`
+
+        const idColumn = platform === 'woocommerce'
+          ? `wc_${singularType}_id`
+          : platform === 'shopify'
+          ? `shopify_${singularType}_id`
+          : `shoprenter_${singularType}_id`
+
+        // Build cache query
+        let cacheQuery = supabase
+          .from(tableName)
+          .select('*')
+          .eq('store_id', storeId)
+          .order('created_at', { ascending: false })
+
+        // Apply search filter
+        if (search) {
+          if (dataType === 'products') {
+            if (platform === 'shoprenter') {
+              cacheQuery = cacheQuery.or(`product_data->>name.ilike.%${search}%,product_data->>sku.ilike.%${search}%`)
+            } else {
+              cacheQuery = cacheQuery.or(`name.ilike.%${search}%,sku.ilike.%${search}%`)
+            }
+          } else if (dataType === 'orders') {
+            cacheQuery = cacheQuery.or(`order_number.ilike.%${search}%,customer_name.ilike.%${search}%,customer_email.ilike.%${search}%`)
+          } else if (dataType === 'customers') {
+            cacheQuery = cacheQuery.or(`email.ilike.%${search}%,first_name.ilike.%${search}%,last_name.ilike.%${search}%,phone.ilike.%${search}%`)
+          }
+        }
+
+        const { data: cacheItems, error: cacheError } = await cacheQuery
+
+        if (cacheError) {
+          console.error(`Error fetching ${dataType} from cache:`, cacheError)
+          return new Response(
+            JSON.stringify({ error: `Failed to fetch ${dataType}` }),
+            { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+          )
+        }
+
+        if (!cacheItems || cacheItems.length === 0) {
+          return new Response(
+            JSON.stringify({
+              success: true,
+              data: [],
+              total: 0,
+              enabled_count: 0,
+              disabled_count: 0
+            }),
+            { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+          )
+        }
+
+        // Get all exclusions for this store and type
+        const { data: exclusions } = await supabase
+          .from('store_data_exclusions')
+          .select('data_id, is_enabled')
+          .eq('store_id', storeId)
+          .eq('data_type', singularType)
+
+        // Create exclusion map
+        const exclusionMap = new Map()
+        if (exclusions) {
+          exclusions.forEach(e => {
+            exclusionMap.set(e.data_id, e.is_enabled)
+          })
+        }
+
+        // Transform cache items to result format
+        let results: any[] = []
+        for (const item of cacheItems) {
+          const itemId = item[idColumn]
+          const isEnabled = exclusionMap.has(itemId) ? exclusionMap.get(itemId) : true
+
+          let resultItem: any = {
+            id: itemId,
+            enabled_in_context: isEnabled
+          }
+
+          // Add type-specific fields
+          if (dataType === 'products') {
+            if (platform === 'shoprenter') {
+              resultItem.name = item.product_data?.name || ''
+              resultItem.sku = item.product_data?.sku || ''
+              resultItem.price = item.product_data?.price || '0'
+              resultItem.currency = item.product_data?.currency || 'HUF'
+            } else {
+              resultItem.name = item.name || item.title || ''
+              resultItem.sku = item.sku || ''
+              resultItem.price = item.price || '0'
+              resultItem.currency = item.currency || 'USD'
+            }
+          } else if (dataType === 'orders') {
+            resultItem.order_number = item.order_number || item.name || ''
+            resultItem.customer_name = item.customer_name || ''
+            resultItem.customer_email = item.customer_email || item.email || ''
+            resultItem.total = item.total || item.total_price || '0'
+            resultItem.currency = item.currency || 'USD'
+          } else if (dataType === 'customers') {
+            resultItem.name = `${item.first_name || ''} ${item.last_name || ''}`.trim()
+            resultItem.email = item.email || ''
+            resultItem.orders_count = item.orders_count || 0
+          }
+
+          results.push(resultItem)
+        }
+
+        // Apply enabled filter if specified
+        if (enabledFilter !== null) {
+          const filterEnabled = enabledFilter === 'true'
+          results = results.filter(r => r.enabled_in_context === filterEnabled)
+        }
+
+        // Calculate counts
+        const enabledCount = results.filter(r => r.enabled_in_context).length
+        const disabledCount = results.filter(r => !r.enabled_in_context).length
+        const totalCount = results.length
+
+        // Apply pagination
+        const offset = (page - 1) * limit
+        const paginatedResults = results.slice(offset, offset + limit)
+
+        return new Response(
+          JSON.stringify({
+            success: true,
+            data: paginatedResults,
+            total: totalCount,
+            enabled_count: enabledCount,
+            disabled_count: disabledCount
+          }),
+          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      } catch (error) {
+        console.error(`Error fetching ${dataType}:`, error)
+        return new Response(
+          JSON.stringify({ error: `Failed to fetch ${dataType}` }),
+          { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+    }
+
+    // PUT /api/store-data/:type/:id/toggle - Toggle individual item
+    if (path.match(/^store-data\/(products|orders|customers)\/[^\/]+\/toggle$/) && req.method === 'PUT') {
+      const pathParts = path.split('/')
+      const dataType = pathParts[1] // products, orders, or customers
+      const itemId = pathParts[2]
+      const { store_id, enabled } = await req.json()
+
+      if (!store_id) {
+        return new Response(
+          JSON.stringify({ error: 'store_id is required' }),
+          { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      // Verify store ownership
+      const { data: store, error: storeError } = await supabase
+        .from('stores')
+        .select('id, platform_name')
+        .eq('id', store_id)
+        .eq('user_id', user.id)
+        .single()
+
+      if (storeError || !store) {
+        return new Response(
+          JSON.stringify({ error: 'Store not found or access denied' }),
+          { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      try {
+        const singularType = dataType === 'products' ? 'product' : dataType === 'orders' ? 'order' : 'customer'
+        const platform = store.platform_name
+
+        // Get metadata from cache
+        const tableName = platform === 'woocommerce'
+          ? `woocommerce_${dataType}_cache`
+          : platform === 'shopify'
+          ? `shopify_${dataType}_cache`
+          : `shoprenter_${dataType}_cache`
+
+        const idColumn = platform === 'woocommerce'
+          ? `wc_${singularType}_id`
+          : platform === 'shopify'
+          ? `shopify_${singularType}_id`
+          : `shoprenter_${singularType}_id`
+
+        const { data: cacheItem } = await supabase
+          .from(tableName)
+          .select('*')
+          .eq('store_id', store_id)
+          .eq(idColumn, itemId)
+          .single()
+
+        let metadata = {}
+        if (cacheItem) {
+          if (singularType === 'product') {
+            if (platform === 'shoprenter') {
+              metadata = {
+                name: cacheItem.product_data?.name,
+                sku: cacheItem.product_data?.sku,
+                price: cacheItem.product_data?.price
+              }
+            } else {
+              metadata = {
+                name: cacheItem.name || cacheItem.title,
+                sku: cacheItem.sku,
+                price: cacheItem.price
+              }
+            }
+          } else if (singularType === 'order') {
+            metadata = {
+              order_number: cacheItem.order_number || cacheItem.name,
+              customer_name: cacheItem.customer_name,
+              total: cacheItem.total || cacheItem.total_price
+            }
+          } else if (singularType === 'customer') {
+            metadata = {
+              email: cacheItem.email,
+              first_name: cacheItem.first_name,
+              last_name: cacheItem.last_name
+            }
+          }
+        }
+
+        // Upsert exclusion
+        const { error: upsertError } = await supabase
+          .from('store_data_exclusions')
+          .upsert({
+            store_id: store_id,
+            data_type: singularType,
+            data_id: itemId,
+            is_enabled: enabled,
+            metadata,
+            updated_at: new Date().toISOString()
+          }, {
+            onConflict: 'store_id,data_type,data_id'
+          })
+
+        if (upsertError) {
+          console.error('Error upserting exclusion:', upsertError)
+          return new Response(
+            JSON.stringify({ error: 'Failed to update item' }),
+            { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+          )
+        }
+
+        return new Response(
+          JSON.stringify({
+            success: true,
+            message: `Item ${enabled ? 'enabled' : 'disabled'} successfully`
+          }),
+          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      } catch (error) {
+        console.error('Error toggling item:', error)
+        return new Response(
+          JSON.stringify({ error: 'Failed to update item' }),
+          { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+    }
+
+    // PUT /api/store-data/:type/bulk-toggle - Bulk toggle items
+    if (path.match(/^store-data\/(products|orders|customers)\/bulk-toggle$/) && req.method === 'PUT') {
+      const dataType = path.split('/')[1]
+      const { store_id, item_ids, enabled } = await req.json()
+
+      if (!store_id || !item_ids || !Array.isArray(item_ids)) {
+        return new Response(
+          JSON.stringify({ error: 'store_id and item_ids array are required' }),
+          { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      // Verify store ownership
+      const { data: store, error: storeError } = await supabase
+        .from('stores')
+        .select('id, platform_name')
+        .eq('id', store_id)
+        .eq('user_id', user.id)
+        .single()
+
+      if (storeError || !store) {
+        return new Response(
+          JSON.stringify({ error: 'Store not found or access denied' }),
+          { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      try {
+        const singularType = dataType === 'products' ? 'product' : dataType === 'orders' ? 'order' : 'customer'
+
+        for (const itemId of item_ids) {
+          await supabase
+            .from('store_data_exclusions')
+            .upsert({
+              store_id: store_id,
+              data_type: singularType,
+              data_id: itemId,
+              is_enabled: enabled,
+              metadata: {},
+              updated_at: new Date().toISOString()
+            }, {
+              onConflict: 'store_id,data_type,data_id'
+            })
+        }
+
+        return new Response(
+          JSON.stringify({
+            success: true,
+            message: `${item_ids.length} items ${enabled ? 'enabled' : 'disabled'} successfully`
+          }),
+          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      } catch (error) {
+        console.error('Error bulk toggling items:', error)
+        return new Response(
+          JSON.stringify({ error: 'Failed to update items' }),
+          { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+    }
+
+    // PUT /api/store-data/:type/enable-all - Enable all items
+    if (path.match(/^store-data\/(products|orders|customers)\/enable-all$/) && req.method === 'PUT') {
+      const dataType = path.split('/')[1]
+      const { store_id } = await req.json()
+
+      if (!store_id) {
+        return new Response(
+          JSON.stringify({ error: 'store_id is required' }),
+          { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      // Verify store ownership
+      const { data: store, error: storeError } = await supabase
+        .from('stores')
+        .select('id')
+        .eq('id', store_id)
+        .eq('user_id', user.id)
+        .single()
+
+      if (storeError || !store) {
+        return new Response(
+          JSON.stringify({ error: 'Store not found or access denied' }),
+          { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      try {
+        const singularType = dataType === 'products' ? 'product' : dataType === 'orders' ? 'order' : 'customer'
+
+        // Update all exclusions for this store and type to enabled
+        const { error: updateError } = await supabase
+          .from('store_data_exclusions')
+          .update({ is_enabled: true, updated_at: new Date().toISOString() })
+          .eq('store_id', store_id)
+          .eq('data_type', singularType)
+
+        if (updateError) {
+          console.error('Error enabling all items:', updateError)
+          return new Response(
+            JSON.stringify({ error: 'Failed to enable all items' }),
+            { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+          )
+        }
+
+        return new Response(
+          JSON.stringify({
+            success: true,
+            message: `All ${dataType} enabled successfully`
+          }),
+          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      } catch (error) {
+        console.error('Error enabling all items:', error)
+        return new Response(
+          JSON.stringify({ error: 'Failed to enable all items' }),
+          { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+    }
+
+    // PUT /api/store-data/:type/disable-all - Disable all items
+    if (path.match(/^store-data\/(products|orders|customers)\/disable-all$/) && req.method === 'PUT') {
+      const dataType = path.split('/')[1]
+      const { store_id } = await req.json()
+
+      if (!store_id) {
+        return new Response(
+          JSON.stringify({ error: 'store_id is required' }),
+          { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      // Verify store ownership
+      const { data: store, error: storeError } = await supabase
+        .from('stores')
+        .select('id, platform_name')
+        .eq('id', store_id)
+        .eq('user_id', user.id)
+        .single()
+
+      if (storeError || !store) {
+        return new Response(
+          JSON.stringify({ error: 'Store not found or access denied' }),
+          { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+
+      try {
+        const singularType = dataType === 'products' ? 'product' : dataType === 'orders' ? 'order' : 'customer'
+        const platform = store.platform_name
+
+        // Get all items from cache to create exclusions
+        const tableName = platform === 'woocommerce'
+          ? `woocommerce_${dataType}_cache`
+          : platform === 'shopify'
+          ? `shopify_${dataType}_cache`
+          : `shoprenter_${dataType}_cache`
+
+        const idColumn = platform === 'woocommerce'
+          ? `wc_${singularType}_id`
+          : platform === 'shopify'
+          ? `shopify_${singularType}_id`
+          : `shoprenter_${singularType}_id`
+
+        const { data: cacheItems } = await supabase
+          .from(tableName)
+          .select(idColumn)
+          .eq('store_id', store_id)
+
+        if (cacheItems && cacheItems.length > 0) {
+          // Create or update exclusions for all items
+          const exclusions = cacheItems.map(item => ({
+            store_id: store_id,
+            data_type: singularType,
+            data_id: item[idColumn],
+            is_enabled: false,
+            metadata: {},
+            updated_at: new Date().toISOString()
+          }))
+
+          const { error: upsertError } = await supabase
+            .from('store_data_exclusions')
+            .upsert(exclusions, {
+              onConflict: 'store_id,data_type,data_id'
+            })
+
+          if (upsertError) {
+            console.error('Error disabling all items:', upsertError)
+            return new Response(
+              JSON.stringify({ error: 'Failed to disable all items' }),
+              { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+            )
+          }
+        }
+
+        return new Response(
+          JSON.stringify({
+            success: true,
+            message: `All ${dataType} disabled successfully`
+          }),
+          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      } catch (error) {
+        console.error('Error disabling all items:', error)
+        return new Response(
+          JSON.stringify({ error: 'Failed to disable all items' }),
+          { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+        )
+      }
+    }
+
     // GET /api/dashboard/stats - Get dashboard statistics
     // GET /api/dashboard/stats - Get dashboard statistics
     if (path === 'dashboard/stats' && req.method === 'GET') {
     if (path === 'dashboard/stats' && req.method === 'GET') {
       // TODO: Implement real dashboard stats calculation
       // TODO: Implement real dashboard stats calculation