Browse Source

refactor: remove deprecated data_access_permissions column and update Edge Functions #98

- Applied migration to drop stores.data_access_permissions column
- Removed data_access_permissions from all store insert operations
- Updated all Edge Functions to use store_sync_config for access policies
- Removed deprecated /permissions API endpoint (use /access-policies instead)
- Updated webshop-data-api and shop-data-api to check store_sync_config policies

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude 5 months ago
parent
commit
938b1c9cff

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

@@ -192,11 +192,6 @@ serve(async (req) => {
           token_expires_at: new Date(Date.now() + (installation.expires_in * 1000)).toISOString(),
           scopes: installation.scopes || [],
           phone_number_id: phoneNumberId,
-          data_access_permissions: {
-            allow_customer_access: true,
-            allow_order_access: true,
-            allow_product_access: true
-          },
           alt_data: {
             token_type: installation.token_type,
             expires_in: installation.expires_in,
@@ -494,78 +489,6 @@ serve(async (req) => {
       )
     }
 
-    // PUT /api/stores/:id/permissions - Update data access permissions for a store
-    if (path.match(/^stores\/[^\/]+\/permissions$/) && req.method === 'PUT') {
-      const storeId = path.split('/')[1]
-      const { data_access_permissions } = await req.json()
-
-      if (!data_access_permissions || typeof data_access_permissions !== 'object') {
-        return new Response(
-          JSON.stringify({ error: 'data_access_permissions object is required' }),
-          { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      // Validate permissions structure
-      const validPermissions = ['allow_customer_access', 'allow_order_access', 'allow_product_access']
-      for (const key of Object.keys(data_access_permissions)) {
-        if (!validPermissions.includes(key)) {
-          return new Response(
-            JSON.stringify({ error: `Invalid permission key: ${key}` }),
-            { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-          )
-        }
-        if (typeof data_access_permissions[key] !== 'boolean') {
-          return new Response(
-            JSON.stringify({ error: `Permission ${key} must be a boolean value` }),
-            { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-          )
-        }
-      }
-
-      // Verify store ownership
-      const { data: store, error: storeError } = await supabase
-        .from('stores')
-        .select('id')
-        .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' } }
-        )
-      }
-
-      // Update store permissions
-      const { error: updateError } = await supabase
-        .from('stores')
-        .update({
-          data_access_permissions,
-          updated_at: new Date().toISOString()
-        })
-        .eq('id', storeId)
-        .eq('user_id', user.id)
-
-      if (updateError) {
-        console.error('Error updating data access permissions:', updateError)
-        return new Response(
-          JSON.stringify({ error: 'Failed to update permissions' }),
-          { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      return new Response(
-        JSON.stringify({
-          success: true,
-          message: 'Data access permissions updated successfully',
-          permissions: data_access_permissions
-        }),
-        { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-      )
-    }
-
     // PUT /api/stores/:id/access-policies - Update data access policies for a store (GDPR compliance)
     if (path.match(/^stores\/[^\/]+\/access-policies$/) && req.method === 'PUT') {
       const storeId = path.split('/')[1]

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

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

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

@@ -234,9 +234,6 @@ serve(wrapHandler('oauth-woocommerce', async (req) => {
           api_secret: consumerSecret,
           phone_number_id: phoneNumberId,
           scopes: ['read'],
-          data_access_permissions: {
-            allow_product_access: true
-          },
           alt_data: {
             wcVersion,
             wpVersion,

+ 16 - 8
supabase/functions/shop-data-api/index.ts

@@ -195,16 +195,23 @@ async function handler(req: Request): Promise<Response> {
     );
   }
 
-  // Fetch store and verify access
-  const storeQuery = supabaseAdmin
+  // Fetch store and verify access (include sync config for access policies)
+  let storeQuery = supabaseAdmin
     .from("stores")
-    .select("*")
+    .select(`
+      *,
+      store_sync_config (
+        products_access_policy,
+        customers_access_policy,
+        orders_access_policy
+      )
+    `)
     .eq("id", storeId)
     .eq("is_active", true);
 
   // For user API keys, restrict to user's stores
   if (authContext.type === "user") {
-    storeQuery.eq("user_id", authContext.userId);
+    storeQuery = storeQuery.eq("user_id", authContext.userId);
   }
 
   const { data: store, error: storeError } = await storeQuery.single();
@@ -218,12 +225,13 @@ async function handler(req: Request): Promise<Response> {
     );
   }
 
-  // Check data access permissions (only for user keys, internal keys have full access)
+  // Check data access policies (only for user keys, internal keys have full access)
   if (authContext.type === "user") {
-    const permissions = store.data_access_permissions || {};
-    const dataType = resource.slice(0, -1); // Remove 's' (customers -> customer)
+    const syncConfig = store.store_sync_config?.[0] || {};
+    const policyKey = `${resource.slice(0, -1)}s_access_policy`; // customers_access_policy, orders_access_policy, products_access_policy
+    const policy = syncConfig[policyKey] || 'not_allowed';
 
-    if (!permissions[`allow_${dataType}_access`]) {
+    if (policy === 'not_allowed') {
       return createErrorResponse(
         "ACCESS_DENIED",
         `Access to ${resource} is not enabled for this store`,

+ 1 - 1
supabase/functions/shopify-sync/index.ts

@@ -845,7 +845,7 @@ serve(wrapHandler('shopify-sync', async (req) => {
 
     const { data: store, error: storeError } = await supabaseAdmin
       .from('stores')
-      .select('id, platform_name, store_name, store_url, qdrant_sync_enabled, data_access_permissions')
+      .select('id, platform_name, store_name, store_url, qdrant_sync_enabled')
       .eq('id', storeId)
       .eq('user_id', user.id)
       .eq('platform_name', 'shopify')

+ 0 - 1
supabase/functions/shoprenter-scheduled-sync/index.ts

@@ -58,7 +58,6 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
         store_url,
         alt_data,
         qdrant_sync_enabled,
-        data_access_permissions,
         store_sync_config (
           enabled,
           sync_frequency,

+ 1 - 2
supabase/functions/shoprenter-sync/index.ts

@@ -700,7 +700,7 @@ serve(wrapHandler('shoprenter-sync', async (req) => {
 
       const { data: store, error: storeError } = await supabaseAdmin
         .from('stores')
-        .select('id, user_id, store_name, platform_name, store_url, qdrant_sync_enabled, data_access_permissions')
+        .select('id, user_id, store_name, platform_name, store_url, qdrant_sync_enabled')
         .eq('id', storeId)
         .eq('platform_name', 'shoprenter')
         .single()
@@ -739,7 +739,6 @@ serve(wrapHandler('shoprenter-sync', async (req) => {
         platform_name,
         store_url,
         qdrant_sync_enabled,
-        data_access_permissions,
         store_sync_config (
           orders_access_policy,
           customers_access_policy

+ 14 - 6
supabase/functions/webshop-data-api/index.ts

@@ -137,10 +137,17 @@ async function handler(req: Request): Promise<Response> {
     );
   }
 
-  // Fetch store and verify ownership
+  // Fetch store and verify ownership (include sync config for access policies)
   const { data: store, error: storeError } = await supabaseAdmin
     .from("stores")
-    .select("*")
+    .select(`
+      *,
+      store_sync_config (
+        products_access_policy,
+        customers_access_policy,
+        orders_access_policy
+      )
+    `)
     .eq("id", storeId)
     .eq("user_id", userId)
     .eq("is_active", true)
@@ -156,11 +163,12 @@ async function handler(req: Request): Promise<Response> {
     );
   }
 
-  // Check data access permissions
-  const permissions = store.data_access_permissions || {};
-  const dataType = resource.slice(0, -1); // Remove 's' (customers -> customer)
+  // Check data access policies from store_sync_config
+  const syncConfig = store.store_sync_config?.[0] || {};
+  const policyKey = `${resource.slice(0, -1)}s_access_policy`; // customers_access_policy, orders_access_policy, products_access_policy
+  const policy = syncConfig[policyKey] || 'not_allowed';
 
-  if (!permissions[`allow_${dataType}_access`]) {
+  if (policy === 'not_allowed') {
     return new Response(
       JSON.stringify({
         error: `Access to ${resource} is not enabled for this store`,

+ 1 - 2
supabase/functions/woocommerce-scheduled-sync/index.ts

@@ -58,8 +58,7 @@ serve(wrapHandler('woocommerce-scheduled-sync', async (req) => {
         user_id,
         store_name,
         store_url,
-        alt_data,
-        data_access_permissions
+        alt_data
       `)
       .eq('platform_name', 'woocommerce')
       .eq('is_active', true)

+ 1 - 1
supabase/functions/woocommerce-sync/index.ts

@@ -913,7 +913,7 @@ serve(wrapHandler('woocommerce-sync', async (req) => {
       // Verify store exists and optionally belongs to user (for non-internal calls)
       let storeQuery = supabaseAdmin
         .from('stores')
-        .select('id, store_name, store_url, qdrant_sync_enabled, data_access_permissions')
+        .select('id, store_name, store_url, qdrant_sync_enabled')
         .eq('id', store_id)
         .eq('platform_name', 'woocommerce')
 

+ 38 - 0
supabase/migrations/20251119_drop_data_access_permissions.sql

@@ -0,0 +1,38 @@
+-- Migration: Drop deprecated data_access_permissions column from stores table
+-- Description: The data_access_permissions column has been replaced by store_sync_config policies
+-- Issue: #98
+
+-- STEP 1: Drop trigger that depends on data_access_permissions
+DROP TRIGGER IF EXISTS trigger_log_permission_change ON stores;
+
+-- STEP 2: Drop the function used by the trigger
+DROP FUNCTION IF EXISTS log_permission_change();
+
+-- STEP 3: Drop constraints that depend on data_access_permissions
+ALTER TABLE stores DROP CONSTRAINT IF EXISTS data_access_permissions_structure;
+ALTER TABLE stores DROP CONSTRAINT IF EXISTS stores_data_access_permissions_check;
+
+-- STEP 4: Drop indexes on data_access_permissions
+DROP INDEX IF EXISTS idx_stores_data_access;
+DROP INDEX IF EXISTS idx_stores_data_access_permissions;
+
+-- STEP 5: Drop the column itself
+ALTER TABLE stores DROP COLUMN IF EXISTS data_access_permissions;
+
+-- STEP 6: Add comment to clarify where access policies are now stored
+COMMENT ON TABLE store_sync_config IS 'Store sync configuration and data access policies. Contains products_access_policy, customers_access_policy, and orders_access_policy which replace the deprecated stores.data_access_permissions column.';
+
+-- Verify the migration was successful
+DO $$
+BEGIN
+  IF EXISTS (
+    SELECT 1 FROM information_schema.columns
+    WHERE table_schema = 'public'
+    AND table_name = 'stores'
+    AND column_name = 'data_access_permissions'
+  ) THEN
+    RAISE EXCEPTION 'Migration failed: data_access_permissions column still exists';
+  ELSE
+    RAISE NOTICE 'Migration successful: data_access_permissions column has been removed from stores table';
+  END IF;
+END $$;