Browse Source

fix: scraper integration path handling and API endpoint construction

- Fix path parsing in scraper-management to handle function name in URL path
- Remove /api prefix from scraper-client endpoints to avoid duplication with base URL
- Ensures correct URL construction when DEFAULT_SCRAPER_API_URL includes /api

The scraper-management Edge Function now correctly strips the 'scraper-management'
segment from the path when called via /functions/v1/scraper-management/endpoint.

The scraper-client now uses relative paths without /api prefix, allowing the base
URL (https://crawler-1.shop.static.shopcall.ai/api) to be properly concatenated.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Fszontagh 4 months ago
parent
commit
06f59cdc20

+ 17 - 17
supabase/functions/_shared/scraper-client.ts

@@ -130,7 +130,7 @@ export class ScraperClient {
    * Register a new shop for scraping
    * Register a new shop for scraping
    */
    */
   async registerShop(url: string, customId: string): Promise<ScraperJob> {
   async registerShop(url: string, customId: string): Promise<ScraperJob> {
-    return await this.makeRequest<ScraperJob>('/api/jobs', {
+    return await this.makeRequest<ScraperJob>('/jobs', {
       method: 'POST',
       method: 'POST',
       body: JSON.stringify({
       body: JSON.stringify({
         url,
         url,
@@ -143,28 +143,28 @@ export class ScraperClient {
    * Get job status by job ID
    * Get job status by job ID
    */
    */
   async getJobStatus(jobId: string): Promise<ScraperJob> {
   async getJobStatus(jobId: string): Promise<ScraperJob> {
-    return await this.makeRequest<ScraperJob>(`/api/jobs/${jobId}`);
+    return await this.makeRequest<ScraperJob>(`/jobs/${jobId}`);
   }
   }
 
 
   /**
   /**
    * List all jobs
    * List all jobs
    */
    */
   async listJobs(): Promise<{ jobs: ScraperJob[], queue_stats: any }> {
   async listJobs(): Promise<{ jobs: ScraperJob[], queue_stats: any }> {
-    return await this.makeRequest<{ jobs: ScraperJob[], queue_stats: any }>('/api/jobs');
+    return await this.makeRequest<{ jobs: ScraperJob[], queue_stats: any }>('/jobs');
   }
   }
 
 
   /**
   /**
    * Get shop information by shop ID (using store_id as custom_id)
    * Get shop information by shop ID (using store_id as custom_id)
    */
    */
   async getShop(shopId: string): Promise<ScraperShop> {
   async getShop(shopId: string): Promise<ScraperShop> {
-    return await this.makeRequest<ScraperShop>(`/api/shops/${shopId}`);
+    return await this.makeRequest<ScraperShop>(`/shops/${shopId}`);
   }
   }
 
 
   /**
   /**
    * List all shops
    * List all shops
    */
    */
   async listShops(): Promise<{ shops: ScraperShop[] }> {
   async listShops(): Promise<{ shops: ScraperShop[] }> {
-    return await this.makeRequest<{ shops: ScraperShop[] }>('/api/shops');
+    return await this.makeRequest<{ shops: ScraperShop[] }>('/shops');
   }
   }
 
 
   /**
   /**
@@ -186,7 +186,7 @@ export class ScraperClient {
       params.append('limit', filter.limit.toString());
       params.append('limit', filter.limit.toString());
     }
     }
 
 
-    const endpoint = `/api/shops/${shopId}/results${params.toString() ? `?${params.toString()}` : ''}`;
+    const endpoint = `/shops/${shopId}/results${params.toString() ? `?${params.toString()}` : ''}`;
     return await this.makeRequest<{ content: ScraperContent[] }>(endpoint);
     return await this.makeRequest<{ content: ScraperContent[] }>(endpoint);
   }
   }
 
 
@@ -194,7 +194,7 @@ export class ScraperClient {
    * Enable or disable scheduled scraping for a shop
    * Enable or disable scheduled scraping for a shop
    */
    */
   async setScheduling(shopId: string, enabled: boolean): Promise<void> {
   async setScheduling(shopId: string, enabled: boolean): Promise<void> {
-    await this.makeRequest(`/api/shops/${shopId}/schedule`, {
+    await this.makeRequest(`/shops/${shopId}/schedule`, {
       method: 'PATCH',
       method: 'PATCH',
       body: JSON.stringify({ enabled }),
       body: JSON.stringify({ enabled }),
     });
     });
@@ -204,7 +204,7 @@ export class ScraperClient {
    * Set or update custom ID for a shop
    * Set or update custom ID for a shop
    */
    */
   async setCustomId(shopId: string, customId: string | null): Promise<void> {
   async setCustomId(shopId: string, customId: string | null): Promise<void> {
-    await this.makeRequest(`/api/shops/${shopId}/custom-id`, {
+    await this.makeRequest(`/shops/${shopId}/custom-id`, {
       method: 'PATCH',
       method: 'PATCH',
       body: JSON.stringify({ custom_id: customId }),
       body: JSON.stringify({ custom_id: customId }),
     });
     });
@@ -214,7 +214,7 @@ export class ScraperClient {
    * Delete a shop and all its data
    * Delete a shop and all its data
    */
    */
   async deleteShop(shopId: string): Promise<void> {
   async deleteShop(shopId: string): Promise<void> {
-    await this.makeRequest(`/api/shops/${shopId}`, {
+    await this.makeRequest(`/shops/${shopId}`, {
       method: 'DELETE',
       method: 'DELETE',
     });
     });
   }
   }
@@ -223,7 +223,7 @@ export class ScraperClient {
    * Configure webhook for a shop
    * Configure webhook for a shop
    */
    */
   async setWebhook(shopId: string, webhookUrl: string, secret?: string): Promise<ScraperWebhook> {
   async setWebhook(shopId: string, webhookUrl: string, secret?: string): Promise<ScraperWebhook> {
-    return await this.makeRequest<ScraperWebhook>(`/api/shops/${shopId}/webhooks`, {
+    return await this.makeRequest<ScraperWebhook>(`/shops/${shopId}/webhooks`, {
       method: 'POST',
       method: 'POST',
       body: JSON.stringify({
       body: JSON.stringify({
         url: webhookUrl,
         url: webhookUrl,
@@ -236,14 +236,14 @@ export class ScraperClient {
    * Get webhook configuration for a shop
    * Get webhook configuration for a shop
    */
    */
   async getWebhook(shopId: string): Promise<ScraperWebhook> {
   async getWebhook(shopId: string): Promise<ScraperWebhook> {
-    return await this.makeRequest<ScraperWebhook>(`/api/shops/${shopId}/webhooks`);
+    return await this.makeRequest<ScraperWebhook>(`/shops/${shopId}/webhooks`);
   }
   }
 
 
   /**
   /**
    * Enable or disable webhook for a shop
    * Enable or disable webhook for a shop
    */
    */
   async setWebhookEnabled(shopId: string, enabled: boolean): Promise<void> {
   async setWebhookEnabled(shopId: string, enabled: boolean): Promise<void> {
-    await this.makeRequest(`/api/shops/${shopId}/webhooks`, {
+    await this.makeRequest(`/shops/${shopId}/webhooks`, {
       method: 'PATCH',
       method: 'PATCH',
       body: JSON.stringify({ enabled }),
       body: JSON.stringify({ enabled }),
     });
     });
@@ -253,7 +253,7 @@ export class ScraperClient {
    * Delete webhook for a shop
    * Delete webhook for a shop
    */
    */
   async deleteWebhook(shopId: string): Promise<void> {
   async deleteWebhook(shopId: string): Promise<void> {
-    await this.makeRequest(`/api/shops/${shopId}/webhooks`, {
+    await this.makeRequest(`/shops/${shopId}/webhooks`, {
       method: 'DELETE',
       method: 'DELETE',
     });
     });
   }
   }
@@ -262,7 +262,7 @@ export class ScraperClient {
    * Add a custom URL to scrape for a shop
    * Add a custom URL to scrape for a shop
    */
    */
   async addCustomUrl(shopId: string, url: string, contentType: ScraperCustomUrl['content_type']): Promise<ScraperCustomUrl> {
   async addCustomUrl(shopId: string, url: string, contentType: ScraperCustomUrl['content_type']): Promise<ScraperCustomUrl> {
-    return await this.makeRequest<ScraperCustomUrl>(`/api/shops/${shopId}/custom-urls`, {
+    return await this.makeRequest<ScraperCustomUrl>(`/shops/${shopId}/custom-urls`, {
       method: 'POST',
       method: 'POST',
       body: JSON.stringify({
       body: JSON.stringify({
         url,
         url,
@@ -275,14 +275,14 @@ export class ScraperClient {
    * List all custom URLs for a shop
    * List all custom URLs for a shop
    */
    */
   async listCustomUrls(shopId: string): Promise<{ custom_urls: ScraperCustomUrl[] }> {
   async listCustomUrls(shopId: string): Promise<{ custom_urls: ScraperCustomUrl[] }> {
-    return await this.makeRequest<{ custom_urls: ScraperCustomUrl[] }>(`/api/shops/${shopId}/custom-urls`);
+    return await this.makeRequest<{ custom_urls: ScraperCustomUrl[] }>(`/shops/${shopId}/custom-urls`);
   }
   }
 
 
   /**
   /**
    * Enable or disable a custom URL
    * Enable or disable a custom URL
    */
    */
   async setCustomUrlEnabled(shopId: string, customUrlId: string, enabled: boolean): Promise<void> {
   async setCustomUrlEnabled(shopId: string, customUrlId: string, enabled: boolean): Promise<void> {
-    await this.makeRequest(`/api/shops/${shopId}/custom-urls/${customUrlId}`, {
+    await this.makeRequest(`/shops/${shopId}/custom-urls/${customUrlId}`, {
       method: 'PATCH',
       method: 'PATCH',
       body: JSON.stringify({ enabled }),
       body: JSON.stringify({ enabled }),
     });
     });
@@ -292,7 +292,7 @@ export class ScraperClient {
    * Delete a custom URL
    * Delete a custom URL
    */
    */
   async deleteCustomUrl(shopId: string, customUrlId: string): Promise<void> {
   async deleteCustomUrl(shopId: string, customUrlId: string): Promise<void> {
-    await this.makeRequest(`/api/shops/${shopId}/custom-urls/${customUrlId}`, {
+    await this.makeRequest(`/shops/${shopId}/custom-urls/${customUrlId}`, {
       method: 'DELETE',
       method: 'DELETE',
     });
     });
   }
   }

+ 6 - 0
supabase/functions/scraper-management/index.ts

@@ -99,6 +99,12 @@ Deno.serve(async (req) => {
     const url = new URL(req.url);
     const url = new URL(req.url);
     const pathParts = url.pathname.split('/').filter(Boolean);
     const pathParts = url.pathname.split('/').filter(Boolean);
 
 
+    // Remove 'scraper-management' from path if present (when called via /functions/v1/scraper-management/...)
+    const functionName = 'scraper-management';
+    if (pathParts.length > 0 && pathParts[0] === functionName) {
+      pathParts.shift();
+    }
+
     // Route to different endpoints based on path
     // Route to different endpoints based on path
     if (pathParts.length < 1) {
     if (pathParts.length < 1) {
       return new Response(
       return new Response(