|
@@ -1,263 +0,0 @@
|
|
|
-/**
|
|
|
|
|
- * Query ShopRenter Edge Function
|
|
|
|
|
- *
|
|
|
|
|
- * Provides a simple query interface for ShopRenter orders and customers.
|
|
|
|
|
- * Supports filtering by email or order_id with automatic token refresh.
|
|
|
|
|
- *
|
|
|
|
|
- * Method: POST
|
|
|
|
|
- * Authentication: Bearer token using internal_api_keys table
|
|
|
|
|
- *
|
|
|
|
|
- * JSON Body Parameters:
|
|
|
|
|
- * - stores_uuid: UUID of the store (required)
|
|
|
|
|
- * - query_type: "order" or "customer" (required)
|
|
|
|
|
- * - filter_by: "email" or "order_id" (required for orders)
|
|
|
|
|
- * - filter_value: The value to filter by (required when filter_by is set)
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
-import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
|
|
|
|
|
-import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
|
|
|
|
|
-import {
|
|
|
|
|
- requireInternalApiKey,
|
|
|
|
|
- createInternalApiKeyErrorResponse
|
|
|
|
|
-} from '../_shared/internal-api-key-auth.ts';
|
|
|
|
|
-import {
|
|
|
|
|
- fetchOrders,
|
|
|
|
|
- fetchCustomers,
|
|
|
|
|
- ShopRenterOrderFilters,
|
|
|
|
|
- ShopRenterCustomerFilters
|
|
|
|
|
-} from '../_shared/shoprenter-client.ts';
|
|
|
|
|
-
|
|
|
|
|
-const corsHeaders = {
|
|
|
|
|
- 'Access-Control-Allow-Origin': '*',
|
|
|
|
|
- 'Access-Control-Allow-Headers': 'authorization, authentication, x-client-info, apikey, content-type',
|
|
|
|
|
- 'Access-Control-Allow-Methods': 'POST, OPTIONS'
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-// Initialize Supabase client
|
|
|
|
|
-const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
|
|
|
|
|
-const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
|
|
|
|
|
-const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
|
|
|
|
-
|
|
|
|
|
-serve(async (req: Request) => {
|
|
|
|
|
- // Handle CORS preflight
|
|
|
|
|
- if (req.method === 'OPTIONS') {
|
|
|
|
|
- return new Response(null, { headers: corsHeaders });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Only allow POST requests
|
|
|
|
|
- if (req.method !== 'POST') {
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'Method not allowed. Use POST.' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 405,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- // Authenticate with internal API key
|
|
|
|
|
- const authResult = await requireInternalApiKey(req, supabase, 'read_orders');
|
|
|
|
|
-
|
|
|
|
|
- if (!authResult.valid) {
|
|
|
|
|
- return createInternalApiKeyErrorResponse(authResult);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Parse JSON body
|
|
|
|
|
- const rawBody = await req.text();
|
|
|
|
|
- console.log('[Query ShopRenter] Raw POST body:', rawBody);
|
|
|
|
|
-
|
|
|
|
|
- let body: any;
|
|
|
|
|
- try {
|
|
|
|
|
- body = JSON.parse(rawBody);
|
|
|
|
|
- console.log('[Query ShopRenter] Parsed JSON body:', body);
|
|
|
|
|
- } catch (parseError: any) {
|
|
|
|
|
- console.error('[Query ShopRenter] Failed to parse JSON body:', parseError.message);
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'Invalid JSON in request body' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 400,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Extract parameters from body
|
|
|
|
|
- const stores_uuid = body.stores_uuid;
|
|
|
|
|
- const query_type = body.query_type;
|
|
|
|
|
- const filter_by = body.filter_by;
|
|
|
|
|
- const filter_value = body.filter_value;
|
|
|
|
|
-
|
|
|
|
|
- // Validate required parameters
|
|
|
|
|
- if (!stores_uuid) {
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'stores_uuid parameter is required' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 400,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (!query_type || !['order', 'customer'].includes(query_type)) {
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'query_type must be either "order" or "customer"' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 400,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Validate filter parameters
|
|
|
|
|
- if (filter_by && !filter_value) {
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'filter_value is required when filter_by is specified' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 400,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Validate filter_by for each query_type
|
|
|
|
|
- if (query_type === 'order' && filter_by && !['email', 'order_id'].includes(filter_by)) {
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'filter_by for orders must be "email" or "order_id"' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 400,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (query_type === 'customer' && filter_by && filter_by !== 'email') {
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'filter_by for customers must be "email"' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 400,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Verify store exists and is ShopRenter
|
|
|
|
|
- const { data: store, error: storeError } = await supabase
|
|
|
|
|
- .from('stores')
|
|
|
|
|
- .select('id, platform_name, store_name, data_access_permissions')
|
|
|
|
|
- .eq('id', stores_uuid)
|
|
|
|
|
- .eq('platform_name', 'shoprenter')
|
|
|
|
|
- .single();
|
|
|
|
|
-
|
|
|
|
|
- if (storeError || !store) {
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'ShopRenter store not found' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 404,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Check permissions
|
|
|
|
|
- const permissions = store.data_access_permissions as any;
|
|
|
|
|
-
|
|
|
|
|
- if (query_type === 'order' && permissions && !permissions.allow_order_access) {
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'Order access not allowed for this store' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 403,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (query_type === 'customer' && permissions && !permissions.allow_customer_access) {
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({ error: 'Customer access not allowed for this store' }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 403,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Execute query based on query_type
|
|
|
|
|
- let result: any;
|
|
|
|
|
-
|
|
|
|
|
- if (query_type === 'order') {
|
|
|
|
|
- // Build filters for orders
|
|
|
|
|
- const filters: ShopRenterOrderFilters = {};
|
|
|
|
|
-
|
|
|
|
|
- if (filter_by === 'email' && filter_value) {
|
|
|
|
|
- filters.email = filter_value;
|
|
|
|
|
- } else if (filter_by === 'order_id' && filter_value) {
|
|
|
|
|
- // Use innerId filter for customer-visible order numbers
|
|
|
|
|
- filters.innerId = filter_value;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- console.log('[Query ShopRenter] Fetching orders with filters:', filters);
|
|
|
|
|
-
|
|
|
|
|
- // Fetch orders from ShopRenter API (token refresh handled automatically)
|
|
|
|
|
- const response = await fetchOrders(stores_uuid, 0, 25, filters);
|
|
|
|
|
- const orders = Array.isArray(response) ? response : (response.items || response.data || response.orders || []);
|
|
|
|
|
-
|
|
|
|
|
- result = {
|
|
|
|
|
- success: true,
|
|
|
|
|
- query_type: 'order',
|
|
|
|
|
- filters_applied: {
|
|
|
|
|
- filter_by,
|
|
|
|
|
- filter_value
|
|
|
|
|
- },
|
|
|
|
|
- count: orders.length,
|
|
|
|
|
- data: orders
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- } else if (query_type === 'customer') {
|
|
|
|
|
- // Build filters for customers
|
|
|
|
|
- const filters: ShopRenterCustomerFilters = {};
|
|
|
|
|
-
|
|
|
|
|
- if (filter_by === 'email' && filter_value) {
|
|
|
|
|
- filters.email = filter_value;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- console.log('[Query ShopRenter] Fetching customers with filters:', filters);
|
|
|
|
|
-
|
|
|
|
|
- // Fetch customers from ShopRenter API (token refresh handled automatically)
|
|
|
|
|
- const response = await fetchCustomers(stores_uuid, 0, 25, filters);
|
|
|
|
|
- const customers = Array.isArray(response) ? response : (response.items || response.data || response.customers || []);
|
|
|
|
|
-
|
|
|
|
|
- result = {
|
|
|
|
|
- success: true,
|
|
|
|
|
- query_type: 'customer',
|
|
|
|
|
- filters_applied: {
|
|
|
|
|
- filter_by,
|
|
|
|
|
- filter_value
|
|
|
|
|
- },
|
|
|
|
|
- count: customers.length,
|
|
|
|
|
- data: customers
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify(result),
|
|
|
|
|
- {
|
|
|
|
|
- status: 200,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- } catch (error: any) {
|
|
|
|
|
- console.error('[Query ShopRenter] Error:', error);
|
|
|
|
|
- return new Response(
|
|
|
|
|
- JSON.stringify({
|
|
|
|
|
- error: 'Internal server error',
|
|
|
|
|
- message: error.message
|
|
|
|
|
- }),
|
|
|
|
|
- {
|
|
|
|
|
- status: 500,
|
|
|
|
|
- headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|