Explorar el Código

fix shoprenter scheduled sync

Fszontagh hace 4 meses
padre
commit
25dfafe337
Se han modificado 1 ficheros con 151 adiciones y 24 borrados
  1. 151 24
      supabase/functions/shoprenter-scheduled-sync/index.ts

+ 151 - 24
supabase/functions/shoprenter-scheduled-sync/index.ts

@@ -1,8 +1,79 @@
 import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
 import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
 import { wrapHandler, logError } from '../_shared/error-handler.ts'
-import { fetchProducts, fetchOrders, fetchCustomers } from '../_shared/shoprenter-client.ts'
+import { fetchProducts, fetchOrders, fetchCustomers, fetchCategory } from '../_shared/shoprenter-client.ts'
 import { detectCountryCode } from '../_shared/phone-formatter.ts'
+import { cleanHtmlContent } from '../_shared/html-cleaner.ts'
+
+/**
+ * Extract category ID from ShopRenter category href URL
+ * Example: "http://shopname.api.myshoprenter.hu/categories/Y2F0ZWdvcnktY2F0ZWdvcnlfaWQ9NTU=" -> "Y2F0ZWdvcnktY2F0ZWdvcnlfaWQ9NTU="
+ */
+function extractCategoryId(categoryHref: string): string | null {
+  if (!categoryHref) return null
+  const parts = categoryHref.split('/')
+  return parts[parts.length - 1] || null
+}
+
+/**
+ * Extract numeric category ID from base64-encoded category ID
+ * Example: "Y2F0ZWdvcnktY2F0ZWdvcnlfaWQ9MjA2" -> "206"
+ */
+function extractNumericCategoryId(base64CategoryId: string): string | null {
+  try {
+    const decoded = atob(base64CategoryId)
+    // Format: "category-category_id=206"
+    const match = decoded.match(/category_id=(\d+)/)
+    return match ? match[1] : null
+  } catch (error) {
+    console.error('Failed to decode category ID:', base64CategoryId, error)
+    return null
+  }
+}
+
+/**
+ * Fetch and process category details from ShopRenter API
+ * Returns category name (HTML cleaned)
+ */
+async function fetchCategoryName(storeId: string, categoryId: string): Promise<string | null> {
+  try {
+    const categoryData = await fetchCategory(storeId, categoryId)
+
+    // Extract first language description (Hungarian is typically first)
+    const categoryDesc = categoryData.categoryDescriptions?.[0]
+    if (!categoryDesc) return null
+
+    const name = categoryDesc.name || ''
+    return name || null
+  } catch (error) {
+    console.error(`[ShopRenter] Failed to fetch category ${categoryId}:`, error)
+    return null
+  }
+}
+
+/**
+ * Extract categories from ShopRenter product and return array with ID and name
+ * Returns: [{ id: "206", name: "Category Name" }, ...]
+ */
+function extractProductCategories(product: any, categoryCache: Map<string, string>): Array<{ id: string; name: string }> {
+  const categories: Array<{ id: string; name: string }> = []
+  const categoryRelations = product.productCategoryRelations || []
+
+  for (const rel of categoryRelations) {
+    const categoryId = extractCategoryId(rel.category?.href)
+    if (categoryId) {
+      const numericId = extractNumericCategoryId(categoryId)
+      if (numericId && categoryCache.has(categoryId)) {
+        categories.push({
+          id: numericId,
+          name: categoryCache.get(categoryId)!
+        })
+      }
+    }
+  }
+
+  return categories
+}
 
 const corsHeaders = {
   'Access-Control-Allow-Origin': '*',
@@ -173,37 +244,19 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
         if (productsPolicy === 'sync') {
           try {
             console.log(`[ShopRenter Scheduled Sync] Syncing products for store ${storeId}`)
+
+            // First, fetch all products to collect category IDs
             let page = 0  // ShopRenter API uses zero-based pagination
             let hasMore = true
             const limit = 50
+            const allProducts: any[] = []
 
+            // Phase 1: Fetch all products
             while (hasMore) {
               const productsData = await fetchProducts(storeId, page, limit)
 
               if (productsData.items && productsData.items.length > 0) {
-                const productsToCache = productsData.items.map((product: any) => ({
-                  store_id: storeId,
-                  product_id: product.id,
-                  inner_id: product.innerId,
-                  name: product.name,
-                  sku: product.sku,
-                  categories: product.productCategory || [],
-                  excluded: false, // Will be set if in exclusion list
-                  last_synced_at: new Date().toISOString()
-                }))
-
-                const { error: upsertError } = await supabaseAdmin
-                  .from('shoprenter_products_cache')
-                  .upsert(productsToCache, {
-                    onConflict: 'store_id,product_id'
-                  })
-
-                if (upsertError) {
-                  console.error(`[ShopRenter Scheduled Sync] Error caching products for store ${storeId}:`, upsertError)
-                  syncStats.products.errors += productsToCache.length
-                } else {
-                  syncStats.products.synced += productsToCache.length
-                }
+                allProducts.push(...productsData.items)
 
                 // Check if there are more pages
                 if (productsData.pagination && productsData.pagination.total) {
@@ -219,6 +272,80 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
               }
             }
 
+            console.log(`[ShopRenter Scheduled Sync] Store ${storeId}: Fetched ${allProducts.length} products, building category cache...`)
+
+            // Phase 2: Build category cache from all products
+            const categoryCache = new Map<string, string>()
+            const uniqueCategoryIds = new Set<string>()
+
+            for (const product of allProducts) {
+              const categoryRelations = product.productCategoryRelations || []
+              for (const rel of categoryRelations) {
+                const categoryId = extractCategoryId(rel.category?.href)
+                if (categoryId && !uniqueCategoryIds.has(categoryId)) {
+                  uniqueCategoryIds.add(categoryId)
+                }
+              }
+            }
+
+            console.log(`[ShopRenter Scheduled Sync] Store ${storeId}: Found ${uniqueCategoryIds.size} unique categories, fetching names...`)
+
+            // Fetch category names (with concurrency limit to avoid rate limiting)
+            const categoryIds = Array.from(uniqueCategoryIds)
+            const batchSize = 5  // Fetch 5 categories at a time
+            for (let i = 0; i < categoryIds.length; i += batchSize) {
+              const batch = categoryIds.slice(i, i + batchSize)
+              await Promise.all(
+                batch.map(async (categoryId) => {
+                  const categoryName = await fetchCategoryName(storeId, categoryId)
+                  if (categoryName) {
+                    categoryCache.set(categoryId, categoryName)
+                  }
+                })
+              )
+            }
+
+            console.log(`[ShopRenter Scheduled Sync] Store ${storeId}: Category cache built with ${categoryCache.size} categories`)
+
+            // Phase 3: Cache products with proper names and categories
+            const productsToCache = allProducts.map((product: any) => {
+              // Extract product name from productDescriptions (localized name)
+              const productDesc = product.productDescriptions?.[0] || {}
+              const productName = productDesc.name || product.name || null
+
+              // Extract categories using the category cache
+              const categories = extractProductCategories(product, categoryCache)
+
+              return {
+                store_id: storeId,
+                product_id: product.id,
+                inner_id: product.innerId,
+                name: productName,
+                sku: product.sku,
+                categories: categories,
+                excluded: false, // Will be set if in exclusion list
+                last_synced_at: new Date().toISOString()
+              }
+            })
+
+            // Batch upsert products (in chunks to avoid large payload issues)
+            const upsertBatchSize = 100
+            for (let i = 0; i < productsToCache.length; i += upsertBatchSize) {
+              const batch = productsToCache.slice(i, i + upsertBatchSize)
+              const { error: upsertError } = await supabaseAdmin
+                .from('shoprenter_products_cache')
+                .upsert(batch, {
+                  onConflict: 'store_id,product_id'
+                })
+
+              if (upsertError) {
+                console.error(`[ShopRenter Scheduled Sync] Error caching products batch for store ${storeId}:`, upsertError)
+                syncStats.products.errors += batch.length
+              } else {
+                syncStats.products.synced += batch.length
+              }
+            }
+
             console.log(`[ShopRenter Scheduled Sync] Store ${storeId}: Products synced: ${syncStats.products.synced}, errors: ${syncStats.products.errors}`)
           } catch (error) {
             console.error(`[ShopRenter Scheduled Sync] Product sync error for store ${storeId}:`, error)