Эх сурвалжийг харах

refact: update OAuth callbacks and scheduled sync for GDPR compliance #48

- Updated ShopRenter scheduled sync to products-only with data access testing
- Added data_access_permissions to all OAuth flows (Shopify, WooCommerce, ShopRenter)
- All new stores now have default permissions (all enabled)
- Test customer/order access without caching personal data
- Fixed syntax errors in Edge Functions (added missing catch blocks)
Claude 5 сар өмнө
parent
commit
3ec206d053

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

@@ -135,6 +135,11 @@ serve(wrapHandler('api', async (req) => {
           api_key: installation.access_token,
           api_key: installation.access_token,
           api_secret: installation.refresh_token,
           api_secret: installation.refresh_token,
           scopes: installation.scopes || [],
           scopes: installation.scopes || [],
+          data_access_permissions: {
+            allow_customer_access: true,
+            allow_order_access: true,
+            allow_product_access: true
+          },
           alt_data: {
           alt_data: {
             token_type: installation.token_type,
             token_type: installation.token_type,
             expires_in: installation.expires_in,
             expires_in: installation.expires_in,
@@ -951,4 +956,12 @@ serve(wrapHandler('api', async (req) => {
       JSON.stringify({ error: 'Not found' }),
       JSON.stringify({ error: 'Not found' }),
       { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
       { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
     )
     )
+
+  } catch (error) {
+    console.error('[API] Unexpected error:', error)
+    return new Response(
+      JSON.stringify({ error: 'Internal server error', details: error.message }),
+      { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+    )
+  }
 }))
 }))

+ 5 - 0
supabase/functions/oauth-shopify/index.ts

@@ -387,6 +387,11 @@ serve(wrapHandler('oauth-shopify', async (req) => {
           store_url: shopDomain,
           store_url: shopDomain,
           api_key: tokenResult.accessToken,
           api_key: tokenResult.accessToken,
           scopes: tokenResult.scopes || SHOPIFY_SCOPES,
           scopes: tokenResult.scopes || SHOPIFY_SCOPES,
+          data_access_permissions: {
+            allow_customer_access: true,
+            allow_order_access: true,
+            allow_product_access: true
+          },
           alt_data: {
           alt_data: {
             shopifyDomain: shopDomain,
             shopifyDomain: shopDomain,
             shop_id: shopData?.id,
             shop_id: shopData?.id,

+ 5 - 0
supabase/functions/oauth-woocommerce/index.ts

@@ -208,6 +208,11 @@ serve(wrapHandler('oauth-woocommerce', async (req) => {
           api_key: consumerKey,
           api_key: consumerKey,
           api_secret: consumerSecret,
           api_secret: consumerSecret,
           scopes: ['read'],
           scopes: ['read'],
+          data_access_permissions: {
+            allow_customer_access: true,
+            allow_order_access: true,
+            allow_product_access: true
+          },
           alt_data: {
           alt_data: {
             wcVersion,
             wcVersion,
             wpVersion,
             wpVersion,

+ 97 - 185
supabase/functions/shoprenter-scheduled-sync/index.ts

@@ -61,8 +61,6 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
           enabled,
           enabled,
           sync_frequency,
           sync_frequency,
           sync_products,
           sync_products,
-          sync_orders,
-          sync_customers,
           last_sync_at,
           last_sync_at,
           next_sync_at
           next_sync_at
         )
         )
@@ -125,8 +123,8 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
         store_id: storeId,
         store_id: storeId,
         store_name: store.store_name,
         store_name: store.store_name,
         products: { synced: 0, errors: 0 },
         products: { synced: 0, errors: 0 },
-        orders: { synced: 0, errors: 0 },
-        customers: { synced: 0, errors: 0 },
+        customer_access: { available: false, tested: false },
+        order_access: { available: false, tested: false },
         started_at: new Date().toISOString(),
         started_at: new Date().toISOString(),
         completed_at: null as string | null,
         completed_at: null as string | null,
         status: 'success' as 'success' | 'partial' | 'failed',
         status: 'success' as 'success' | 'partial' | 'failed',
@@ -134,195 +132,102 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
       }
       }
 
 
       try {
       try {
-        // Sync Products
-        try {
-          console.log(`[ShopRenter Scheduled Sync] Syncing products for store ${storeId}`)
-          let page = 1
-          let hasMore = true
-          const limit = 50
-
-          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,
-                shoprenter_product_id: product.id,
-                name: product.name,
-                sku: product.sku,
-                price: parseFloat(product.price) || 0,
-                currency: product.currency || 'HUF',
-                description: product.description,
-                stock: product.stock,
-                active: product.active !== false,
-                raw_data: product,
-                last_synced_at: new Date().toISOString()
-              }))
-
-              const { error: upsertError } = await supabaseAdmin
-                .from('shoprenter_products_cache')
-                .upsert(productsToCache, {
-                  onConflict: 'store_id,shoprenter_product_id'
-                })
-
-              if (upsertError) {
-                console.error(`[ShopRenter Scheduled Sync] Error caching products for store ${storeId}:`, upsertError)
-                syncStats.products.errors += productsToCache.length
+        // Sync Products (only data type we cache now for GDPR compliance)
+        if (config?.sync_products !== false) {
+          try {
+            console.log(`[ShopRenter Scheduled Sync] Syncing products for store ${storeId}`)
+            let page = 1
+            let hasMore = true
+            const limit = 50
+
+            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,
+                  shoprenter_product_id: product.id,
+                  name: product.name,
+                  sku: product.sku,
+                  price: parseFloat(product.price) || 0,
+                  currency: product.currency || 'HUF',
+                  description: product.description,
+                  stock: product.stock,
+                  active: product.active !== false,
+                  raw_data: product,
+                  last_synced_at: new Date().toISOString()
+                }))
+
+                const { error: upsertError } = await supabaseAdmin
+                  .from('shoprenter_products_cache')
+                  .upsert(productsToCache, {
+                    onConflict: 'store_id,shoprenter_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
+                }
+
+                // Check if there are more pages
+                if (productsData.pagination && productsData.pagination.total) {
+                  const totalPages = Math.ceil(productsData.pagination.total / limit)
+                  hasMore = page < totalPages
+                } else {
+                  hasMore = productsData.items.length === limit
+                }
+
+                page++
               } else {
               } else {
-                syncStats.products.synced += productsToCache.length
+                hasMore = false
               }
               }
-
-              // Check if there are more pages
-              if (productsData.pagination && productsData.pagination.total) {
-                const totalPages = Math.ceil(productsData.pagination.total / limit)
-                hasMore = page < totalPages
-              } else {
-                hasMore = productsData.items.length === limit
-              }
-
-              page++
-            } else {
-              hasMore = false
             }
             }
-          }
 
 
-          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)
-          syncStats.products.errors++
-          syncStats.status = 'partial'
+            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)
+            syncStats.products.errors++
+            syncStats.status = 'partial'
+          }
         }
         }
 
 
-        // Sync Orders
-        try {
-          console.log(`[ShopRenter Scheduled Sync] Syncing orders for store ${storeId}`)
-          let page = 1
-          let hasMore = true
-          const limit = 50
-
-          while (hasMore) {
-            const ordersData = await fetchOrders(storeId, page, limit)
-
-            if (ordersData.items && ordersData.items.length > 0) {
-              const ordersToCache = ordersData.items.map((order: any) => ({
-                store_id: storeId,
-                shoprenter_order_id: order.id,
-                order_number: order.order_number || order.number || order.id,
-                status: order.status,
-                total: parseFloat(order.total) || 0,
-                currency: order.currency || 'HUF',
-                customer_name: order.customer_name || `${order.customer?.firstname || ''} ${order.customer?.lastname || ''}`.trim() || null,
-                customer_email: order.customer_email || order.customer?.email || null,
-                customer_phone: formatFirstValidPhone([
-                  order.customer_phone,
-                  order.customer?.phone,
-                  order.billing_address?.phone,
-                  order.shipping_address?.phone
-                ], countryCode),
-                line_items: order.items || order.line_items || [],
-                billing_address: order.billing_address || null,
-                shipping_address: order.shipping_address || null,
-                order_created_at: order.created_at || order.date_created || new Date().toISOString(),
-                raw_data: order,
-                last_synced_at: new Date().toISOString()
-              }))
-
-              const { error: upsertError } = await supabaseAdmin
-                .from('shoprenter_orders_cache')
-                .upsert(ordersToCache, {
-                  onConflict: 'store_id,shoprenter_order_id'
-                })
-
-              if (upsertError) {
-                console.error(`[ShopRenter Scheduled Sync] Error caching orders for store ${storeId}:`, upsertError)
-                syncStats.orders.errors += ordersToCache.length
-              } else {
-                syncStats.orders.synced += ordersToCache.length
-              }
-
-              // Check if there are more pages
-              if (ordersData.pagination && ordersData.pagination.total) {
-                const totalPages = Math.ceil(ordersData.pagination.total / limit)
-                hasMore = page < totalPages
-              } else {
-                hasMore = ordersData.items.length === limit
-              }
-
-              page++
-            } else {
-              hasMore = false
-            }
+        // Test customer data access (don't sync, just validate)
+        // Check if store has data_access_permissions.allow_customer_access enabled
+        const { data: storeData } = await supabaseAdmin
+          .from('stores')
+          .select('data_access_permissions')
+          .eq('id', storeId)
+          .single()
+
+        if (storeData?.data_access_permissions?.allow_customer_access) {
+          try {
+            console.log(`[ShopRenter Scheduled Sync] Testing customer data access for store ${storeId}`)
+            const customersTest = await fetchCustomers(storeId, 1, 1)
+            syncStats.customer_access.tested = true
+            syncStats.customer_access.available = !!(customersTest && customersTest.items)
+            console.log(`[ShopRenter Scheduled Sync] Store ${storeId}: Customer access available: ${syncStats.customer_access.available}`)
+          } catch (error) {
+            console.error(`[ShopRenter Scheduled Sync] Customer access test error for store ${storeId}:`, error)
+            syncStats.customer_access.tested = true
+            syncStats.customer_access.available = false
           }
           }
-
-          console.log(`[ShopRenter Scheduled Sync] Store ${storeId}: Orders synced: ${syncStats.orders.synced}, errors: ${syncStats.orders.errors}`)
-        } catch (error) {
-          console.error(`[ShopRenter Scheduled Sync] Order sync error for store ${storeId}:`, error)
-          syncStats.orders.errors++
-          syncStats.status = 'partial'
         }
         }
 
 
-        // Sync Customers
-        try {
-          console.log(`[ShopRenter Scheduled Sync] Syncing customers for store ${storeId}`)
-          let page = 1
-          let hasMore = true
-          const limit = 50
-
-          while (hasMore) {
-            const customersData = await fetchCustomers(storeId, page, limit)
-
-            if (customersData.items && customersData.items.length > 0) {
-              const customersToCache = customersData.items.map((customer: any) => ({
-                store_id: storeId,
-                shoprenter_customer_id: customer.id,
-                email: customer.email,
-                first_name: customer.firstname,
-                last_name: customer.lastname,
-                phone: formatFirstValidPhone([
-                  customer.phone,
-                  customer.billing_address?.phone,
-                  customer.shipping_address?.phone
-                ], countryCode),
-                billing_address: customer.billing_address || null,
-                shipping_address: customer.shipping_address || null,
-                orders_count: customer.orders_count || 0,
-                total_spent: parseFloat(customer.total_spent) || 0,
-                raw_data: customer,
-                last_synced_at: new Date().toISOString()
-              }))
-
-              const { error: upsertError } = await supabaseAdmin
-                .from('shoprenter_customers_cache')
-                .upsert(customersToCache, {
-                  onConflict: 'store_id,shoprenter_customer_id'
-                })
-
-              if (upsertError) {
-                console.error(`[ShopRenter Scheduled Sync] Error caching customers for store ${storeId}:`, upsertError)
-                syncStats.customers.errors += customersToCache.length
-              } else {
-                syncStats.customers.synced += customersToCache.length
-              }
-
-              // Check if there are more pages
-              if (customersData.pagination && customersData.pagination.total) {
-                const totalPages = Math.ceil(customersData.pagination.total / limit)
-                hasMore = page < totalPages
-              } else {
-                hasMore = customersData.items.length === limit
-              }
-
-              page++
-            } else {
-              hasMore = false
-            }
+        // Test order data access (don't sync, just validate)
+        if (storeData?.data_access_permissions?.allow_order_access) {
+          try {
+            console.log(`[ShopRenter Scheduled Sync] Testing order data access for store ${storeId}`)
+            const ordersTest = await fetchOrders(storeId, 1, 1)
+            syncStats.order_access.tested = true
+            syncStats.order_access.available = !!(ordersTest && ordersTest.items)
+            console.log(`[ShopRenter Scheduled Sync] Store ${storeId}: Order access available: ${syncStats.order_access.available}`)
+          } catch (error) {
+            console.error(`[ShopRenter Scheduled Sync] Order access test error for store ${storeId}:`, error)
+            syncStats.order_access.tested = true
+            syncStats.order_access.available = false
           }
           }
-
-          console.log(`[ShopRenter Scheduled Sync] Store ${storeId}: Customers synced: ${syncStats.customers.synced}, errors: ${syncStats.customers.errors}`)
-        } catch (error) {
-          console.error(`[ShopRenter Scheduled Sync] Customer sync error for store ${storeId}:`, error)
-          syncStats.customers.errors++
-          syncStats.status = 'partial'
         }
         }
 
 
         // Update store last_sync timestamp
         // Update store last_sync timestamp
@@ -331,8 +236,8 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
           last_sync_at: new Date().toISOString(),
           last_sync_at: new Date().toISOString(),
           last_sync_stats: {
           last_sync_stats: {
             products: syncStats.products,
             products: syncStats.products,
-            orders: syncStats.orders,
-            customers: syncStats.customers
+            customer_access: syncStats.customer_access,
+            order_access: syncStats.order_access
           },
           },
           last_sync_type: 'scheduled'
           last_sync_type: 'scheduled'
         }
         }
@@ -415,4 +320,11 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
       { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
       { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
     )
     )
 
 
+  } catch (error) {
+    console.error('[ShopRenter Scheduled Sync] Unexpected error:', error)
+    return new Response(
+      JSON.stringify({ error: 'Internal server error', details: error.message }),
+      { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
+    )
+  }
 }))
 }))