Browse Source

fix: remove OAuth authentication from WooCommerce integration, keep only manual API key connection #18

Claude 5 months ago
parent
commit
d9b2a07

+ 98 - 230
shopcall.ai-main/src/components/WooCommerceConnect.tsx

@@ -4,7 +4,6 @@ import { Button } from "@/components/ui/button";
 import { Input } from "@/components/ui/input";
 import { Input } from "@/components/ui/input";
 import { Label } from "@/components/ui/label";
 import { Label } from "@/components/ui/label";
 import { Alert, AlertDescription } from "@/components/ui/alert";
 import { Alert, AlertDescription } from "@/components/ui/alert";
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
 import { Loader2, ShoppingBag, ExternalLink, CheckCircle2, AlertCircle, Key } from "lucide-react";
 import { Loader2, ShoppingBag, ExternalLink, CheckCircle2, AlertCircle, Key } from "lucide-react";
 import { API_URL } from "@/lib/config";
 import { API_URL } from "@/lib/config";
 
 
@@ -21,89 +20,6 @@ export function WooCommerceConnect({ onClose }: WooCommerceConnectProps) {
   const [success, setSuccess] = useState(false);
   const [success, setSuccess] = useState(false);
   const [successMessage, setSuccessMessage] = useState("");
   const [successMessage, setSuccessMessage] = useState("");
 
 
-  const handleOAuthConnect = async () => {
-    setError("");
-    setSuccess(false);
-
-    // Validate store URL
-    if (!storeUrl.trim()) {
-      setError("Please enter your WooCommerce store URL");
-      return;
-    }
-
-    // Normalize URL
-    let normalizedUrl = storeUrl.trim();
-
-    // Remove trailing slash
-    normalizedUrl = normalizedUrl.replace(/\/$/, "");
-
-    // Add https:// if missing
-    if (!normalizedUrl.startsWith('http://') && !normalizedUrl.startsWith('https://')) {
-      normalizedUrl = `https://${normalizedUrl}`;
-    }
-
-    // Validate URL format
-    try {
-      const url = new URL(normalizedUrl);
-      if (url.protocol !== 'https:') {
-        setError("Store URL must use HTTPS for security");
-        return;
-      }
-    } catch (e) {
-      setError("Please enter a valid store URL (e.g., https://yourstore.com)");
-      return;
-    }
-
-    setIsConnecting(true);
-
-    try {
-      // Get auth token
-      const sessionData = localStorage.getItem('session_data');
-      if (!sessionData) {
-        setError("Authentication required. Please log in again.");
-        setIsConnecting(false);
-        return;
-      }
-
-      const session = JSON.parse(sessionData);
-
-      // Call the OAuth initiation Edge Function
-      const response = await fetch(
-        `${API_URL}/oauth-woocommerce?action=init&store_url=${encodeURIComponent(normalizedUrl)}`,
-        {
-          method: 'GET',
-          headers: {
-            'Authorization': `Bearer ${session.session.access_token}`,
-            'Content-Type': 'application/json'
-          }
-        }
-      );
-
-      if (!response.ok) {
-        const errorData = await response.json();
-        throw new Error(errorData.error || 'Failed to initiate OAuth flow');
-      }
-
-      const data = await response.json();
-
-      if (data.success && data.authUrl) {
-        // Redirect to WooCommerce for authorization
-        setSuccess(true);
-        setSuccessMessage("Redirecting to WooCommerce for authorization...");
-        setTimeout(() => {
-          window.location.href = data.authUrl;
-        }, 1000);
-      } else {
-        throw new Error('Invalid response from server');
-      }
-
-    } catch (err) {
-      console.error("Connection error:", err);
-      setError(err instanceof Error ? err.message : "Failed to connect to WooCommerce. Please try again.");
-      setIsConnecting(false);
-    }
-  };
-
   const handleManualConnect = async () => {
   const handleManualConnect = async () => {
     setError("");
     setError("");
     setSuccess(false);
     setSuccess(false);
@@ -240,31 +156,19 @@ export function WooCommerceConnect({ onClose }: WooCommerceConnectProps) {
           </Alert>
           </Alert>
         )}
         )}
 
 
-        {/* Connection Methods */}
-        <Tabs defaultValue="oauth" className="w-full">
-          <TabsList className="grid w-full grid-cols-2 bg-slate-700">
-            <TabsTrigger value="oauth" className="data-[state=active]:bg-slate-600">
-              OAuth (Recommended)
-            </TabsTrigger>
-            <TabsTrigger value="manual" className="data-[state=active]:bg-slate-600">
-              <Key className="w-4 h-4 mr-2" />
-              API Keys
-            </TabsTrigger>
-          </TabsList>
-
-          {/* OAuth Method */}
-          <TabsContent value="oauth" className="space-y-4 mt-4">
+        {/* Manual API Keys Connection Form */}
+        <div className="space-y-4">
+          <div className="space-y-4">
             <div className="space-y-2">
             <div className="space-y-2">
-              <Label htmlFor="storeUrl" className="text-white">
+              <Label htmlFor="manualStoreUrl" className="text-white">
                 WooCommerce Store URL
                 WooCommerce Store URL
               </Label>
               </Label>
               <Input
               <Input
-                id="storeUrl"
+                id="manualStoreUrl"
                 type="text"
                 type="text"
                 placeholder="https://yourstore.com"
                 placeholder="https://yourstore.com"
                 value={storeUrl}
                 value={storeUrl}
                 onChange={(e) => setStoreUrl(e.target.value)}
                 onChange={(e) => setStoreUrl(e.target.value)}
-                onKeyPress={(e) => e.key === "Enter" && !isConnecting && handleOAuthConnect()}
                 className="bg-slate-700 border-slate-600 text-white placeholder:text-slate-400"
                 className="bg-slate-700 border-slate-600 text-white placeholder:text-slate-400"
                 disabled={isConnecting}
                 disabled={isConnecting}
               />
               />
@@ -273,137 +177,101 @@ export function WooCommerceConnect({ onClose }: WooCommerceConnectProps) {
               </p>
               </p>
             </div>
             </div>
 
 
-            <Button
-              onClick={handleOAuthConnect}
-              disabled={isConnecting}
-              className="w-full bg-purple-500 hover:bg-purple-600 text-white"
-            >
-              {isConnecting ? (
-                <>
-                  <Loader2 className="w-4 h-4 mr-2 animate-spin" />
-                  Connecting...
-                </>
-              ) : (
-                <>
-                  <ShoppingBag className="w-4 h-4 mr-2" />
-                  Connect via OAuth
-                </>
-              )}
-            </Button>
-
-            <div className="bg-slate-700/50 rounded-lg p-4 space-y-3">
-              <h4 className="text-white font-medium flex items-center gap-2">
-                <ExternalLink className="w-4 h-4 text-purple-500" />
-                What happens next?
-              </h4>
-              <ul className="space-y-2 text-sm text-slate-300">
-                <li className="flex items-start gap-2">
-                  <span className="text-purple-500 mt-0.5">1.</span>
-                  <span>You'll be redirected to your WooCommerce admin panel</span>
-                </li>
-                <li className="flex items-start gap-2">
-                  <span className="text-purple-500 mt-0.5">2.</span>
-                  <span>Review and approve the connection request</span>
-                </li>
-                <li className="flex items-start gap-2">
-                  <span className="text-purple-500 mt-0.5">3.</span>
-                  <span>WooCommerce will generate secure API credentials</span>
-                </li>
-                <li className="flex items-start gap-2">
-                  <span className="text-purple-500 mt-0.5">4.</span>
-                  <span>Return to ShopCall.ai to complete your setup</span>
-                </li>
-              </ul>
+            <div className="space-y-2">
+              <Label htmlFor="consumerKey" className="text-white">
+                Consumer Key
+              </Label>
+              <Input
+                id="consumerKey"
+                type="text"
+                placeholder="ck_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+                value={consumerKey}
+                onChange={(e) => setConsumerKey(e.target.value)}
+                className="bg-slate-700 border-slate-600 text-white placeholder:text-slate-400 font-mono text-sm"
+                disabled={isConnecting}
+              />
             </div>
             </div>
-          </TabsContent>
-
-          {/* Manual API Keys Method */}
-          <TabsContent value="manual" className="space-y-4 mt-4">
-            <div className="space-y-4">
-              <div className="space-y-2">
-                <Label htmlFor="manualStoreUrl" className="text-white">
-                  WooCommerce Store URL
-                </Label>
-                <Input
-                  id="manualStoreUrl"
-                  type="text"
-                  placeholder="https://yourstore.com"
-                  value={storeUrl}
-                  onChange={(e) => setStoreUrl(e.target.value)}
-                  className="bg-slate-700 border-slate-600 text-white placeholder:text-slate-400"
-                  disabled={isConnecting}
-                />
-              </div>
 
 
-              <div className="space-y-2">
-                <Label htmlFor="consumerKey" className="text-white">
-                  Consumer Key
-                </Label>
-                <Input
-                  id="consumerKey"
-                  type="text"
-                  placeholder="ck_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-                  value={consumerKey}
-                  onChange={(e) => setConsumerKey(e.target.value)}
-                  className="bg-slate-700 border-slate-600 text-white placeholder:text-slate-400 font-mono text-sm"
-                  disabled={isConnecting}
-                />
-              </div>
-
-              <div className="space-y-2">
-                <Label htmlFor="consumerSecret" className="text-white">
-                  Consumer Secret
-                </Label>
-                <Input
-                  id="consumerSecret"
-                  type="password"
-                  placeholder="cs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-                  value={consumerSecret}
-                  onChange={(e) => setConsumerSecret(e.target.value)}
-                  onKeyPress={(e) => e.key === "Enter" && !isConnecting && handleManualConnect()}
-                  className="bg-slate-700 border-slate-600 text-white placeholder:text-slate-400 font-mono text-sm"
-                  disabled={isConnecting}
-                />
-              </div>
+            <div className="space-y-2">
+              <Label htmlFor="consumerSecret" className="text-white">
+                Consumer Secret
+              </Label>
+              <Input
+                id="consumerSecret"
+                type="password"
+                placeholder="cs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+                value={consumerSecret}
+                onChange={(e) => setConsumerSecret(e.target.value)}
+                onKeyPress={(e) => e.key === "Enter" && !isConnecting && handleManualConnect()}
+                className="bg-slate-700 border-slate-600 text-white placeholder:text-slate-400 font-mono text-sm"
+                disabled={isConnecting}
+              />
             </div>
             </div>
+          </div>
 
 
-            <Button
-              onClick={handleManualConnect}
-              disabled={isConnecting}
-              className="w-full bg-purple-500 hover:bg-purple-600 text-white"
-            >
-              {isConnecting ? (
-                <>
-                  <Loader2 className="w-4 h-4 mr-2 animate-spin" />
-                  Connecting...
-                </>
-              ) : (
-                <>
-                  <Key className="w-4 h-4 mr-2" />
-                  Connect with API Keys
-                </>
-              )}
-            </Button>
+          <Button
+            onClick={handleManualConnect}
+            disabled={isConnecting}
+            className="w-full bg-purple-500 hover:bg-purple-600 text-white"
+          >
+            {isConnecting ? (
+              <>
+                <Loader2 className="w-4 h-4 mr-2 animate-spin" />
+                Connecting...
+              </>
+            ) : (
+              <>
+                <Key className="w-4 h-4 mr-2" />
+                Connect WooCommerce Store
+              </>
+            )}
+          </Button>
 
 
-            <div className="bg-blue-500/10 border border-blue-500/50 rounded-lg p-4 space-y-2">
-              <h4 className="text-white font-medium flex items-center gap-2">
-                <ExternalLink className="w-4 h-4 text-blue-500" />
-                How to generate API keys
-              </h4>
-              <ol className="space-y-2 text-sm text-slate-300 list-decimal list-inside">
-                <li>Go to WooCommerce → Settings → Advanced → REST API</li>
-                <li>Click "Add key"</li>
-                <li>Set description: "ShopCall.ai"</li>
-                <li>Set permissions: "Read"</li>
-                <li>Click "Generate API key"</li>
-                <li>Copy the Consumer Key and Consumer Secret</li>
-              </ol>
-              <p className="text-xs text-slate-400 mt-2">
-                Make sure to copy both keys immediately - WooCommerce shows the Consumer Secret only once!
-              </p>
-            </div>
-          </TabsContent>
-        </Tabs>
+          <div className="bg-blue-500/10 border border-blue-500/50 rounded-lg p-4 space-y-3">
+            <h4 className="text-white font-medium flex items-center gap-2">
+              <ExternalLink className="w-4 h-4 text-blue-500" />
+              How to generate WooCommerce API keys
+            </h4>
+            <ol className="space-y-2 text-sm text-slate-300">
+              <li className="flex items-start gap-2">
+                <span className="text-blue-500 font-semibold min-w-[20px]">1.</span>
+                <span>Log into your WooCommerce admin panel</span>
+              </li>
+              <li className="flex items-start gap-2">
+                <span className="text-blue-500 font-semibold min-w-[20px]">2.</span>
+                <span>Navigate to <strong>WooCommerce → Settings → Advanced → REST API</strong></span>
+              </li>
+              <li className="flex items-start gap-2">
+                <span className="text-blue-500 font-semibold min-w-[20px]">3.</span>
+                <span>Click <strong>"Add key"</strong> button</span>
+              </li>
+              <li className="flex items-start gap-2">
+                <span className="text-blue-500 font-semibold min-w-[20px]">4.</span>
+                <span>Fill in the form:
+                  <ul className="ml-4 mt-1 space-y-1">
+                    <li>• Description: <code className="text-purple-400">ShopCall.ai</code></li>
+                    <li>• User: <em>(select your admin user)</em></li>
+                    <li>• Permissions: <code className="text-purple-400">Read</code></li>
+                  </ul>
+                </span>
+              </li>
+              <li className="flex items-start gap-2">
+                <span className="text-blue-500 font-semibold min-w-[20px]">5.</span>
+                <span>Click <strong>"Generate API key"</strong></span>
+              </li>
+              <li className="flex items-start gap-2">
+                <span className="text-blue-500 font-semibold min-w-[20px]">6.</span>
+                <span>Copy both the <strong>Consumer Key</strong> and <strong>Consumer Secret</strong></span>
+              </li>
+            </ol>
+            <Alert className="bg-yellow-500/10 border-yellow-500/50 mt-3">
+              <AlertCircle className="h-4 w-4 text-yellow-500" />
+              <AlertDescription className="text-yellow-500 text-xs">
+                Important: WooCommerce shows the Consumer Secret only once! Make sure to copy it before closing the page.
+              </AlertDescription>
+            </Alert>
+          </div>
+        </div>
 
 
         {/* Security Notice */}
         {/* Security Notice */}
         <div className="bg-blue-500/10 border border-blue-500/50 rounded-lg p-4 space-y-2">
         <div className="bg-blue-500/10 border border-blue-500/50 rounded-lg p-4 space-y-2">
@@ -412,9 +280,9 @@ export function WooCommerceConnect({ onClose }: WooCommerceConnectProps) {
             <div className="space-y-1">
             <div className="space-y-1">
               <h4 className="text-white font-medium">Secure Connection</h4>
               <h4 className="text-white font-medium">Secure Connection</h4>
               <p className="text-sm text-slate-300">
               <p className="text-sm text-slate-300">
-                ShopCall.ai uses OAuth 1.0a authentication with read-only access to your store data.
-                Your credentials are encrypted and stored securely. We never have access to your
-                WordPress admin password.
+                ShopCall.ai uses WooCommerce REST API with read-only access to your store data.
+                Your API credentials are encrypted and stored securely. We never have access to your
+                WordPress admin password. All connections use HTTPS for maximum security.
               </p>
               </p>
             </div>
             </div>
           </div>
           </div>

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

@@ -131,223 +131,6 @@ serve(async (req) => {
     const supabaseUrl = Deno.env.get('SUPABASE_URL')!
     const supabaseUrl = Deno.env.get('SUPABASE_URL')!
     const supabaseKey = Deno.env.get('SUPABASE_ANON_KEY')!
     const supabaseKey = Deno.env.get('SUPABASE_ANON_KEY')!
     const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
     const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
-    const frontendUrl = Deno.env.get('FRONTEND_URL') || 'https://shopcall.ai'
-
-    // Detect WooCommerce OAuth callback: POST request without action parameter
-    // WooCommerce sends POST with form-urlencoded OAuth credentials
-    const isWooCommerceCallback = req.method === 'POST' && !action
-
-    // Handle OAuth callback from WooCommerce
-    if (isWooCommerceCallback) {
-      console.log('[WooCommerce] Detected OAuth callback via POST request')
-
-      // Parse form-urlencoded POST body
-      let success: string | null = null
-      let userId: string | null = null
-      let consumerKey: string | null = null
-      let consumerSecret: string | null = null
-      let storeUrlParam: string | null = null
-
-      const contentType = req.headers.get('content-type') || ''
-
-      if (contentType.includes('application/x-www-form-urlencoded')) {
-        const body = await req.text()
-        const params = new URLSearchParams(body)
-        success = params.get('success')
-        userId = params.get('user_id')
-        consumerKey = params.get('consumer_key')
-        consumerSecret = params.get('consumer_secret')
-        storeUrlParam = params.get('store_url')
-
-        console.log(`[WooCommerce] Callback params - success: ${success}, userId: ${userId}, hasKeys: ${!!consumerKey && !!consumerSecret}`)
-      } else if (contentType.includes('application/json')) {
-        const body = await req.json()
-        success = body.success
-        userId = body.user_id
-        consumerKey = body.consumer_key
-        consumerSecret = body.consumer_secret
-        storeUrlParam = body.store_url
-      }
-
-      if (success !== '1') {
-        console.error('[WooCommerce] OAuth rejected by user')
-        return new Response(
-          JSON.stringify({ success: false, message: 'OAuth rejected by user' }),
-          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      if (!userId || !consumerKey || !consumerSecret || !storeUrlParam) {
-        console.error('[WooCommerce] Missing required callback parameters')
-        return new Response(
-          JSON.stringify({ success: false, message: 'Missing required parameters' }),
-          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      // Validate store URL
-      const validation = validateStoreUrl(storeUrlParam)
-      if (!validation.valid) {
-        console.error('[WooCommerce] Invalid store URL:', validation.error)
-        return new Response(
-          JSON.stringify({ success: false, message: validation.error }),
-          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      // Test API connection
-      const testResult = await testWooCommerceConnection(
-        validation.normalized!,
-        consumerKey,
-        consumerSecret
-      )
-
-      if (!testResult.success) {
-        console.error('[WooCommerce] API connection test failed:', testResult.error)
-        return new Response(
-          JSON.stringify({ success: false, message: testResult.error }),
-          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      // Extract store info from API response
-      const systemStatus = testResult.data
-      const wcVersion = systemStatus?.environment?.version || 'unknown'
-      const wpVersion = systemStatus?.environment?.wp_version || 'unknown'
-      const storeName = systemStatus?.settings?.site_title?.value || new URL(validation.normalized!).hostname
-
-      // Create Supabase admin client
-      const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey)
-
-      // Store credentials in database
-      const { error: insertError } = await supabaseAdmin
-        .from('stores')
-        .insert({
-          user_id: userId,
-          platform_name: 'woocommerce',
-          store_name: storeName,
-          store_url: validation.normalized,
-          api_key: consumerKey,
-          api_secret: consumerSecret,
-          scopes: ['read'],
-          alt_data: {
-            wcVersion,
-            wpVersion,
-            apiVersion: 'wc/v3',
-            connectedAt: new Date().toISOString(),
-            authMethod: 'oauth'
-          }
-        })
-
-      if (insertError) {
-        console.error('[WooCommerce] Error storing credentials:', insertError)
-        return new Response(
-          JSON.stringify({ success: false, message: 'Failed to save credentials' }),
-          { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      console.log(`[WooCommerce] Store connected successfully: ${storeName}`)
-
-      // Return 200 OK with success message
-      // WooCommerce will redirect the user to the return_url
-      return new Response(
-        JSON.stringify({ success: true, message: 'Store connected successfully', storeName }),
-        { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-      )
-    }
-
-    // Handle OAuth initiation
-    if (action === 'init') {
-      const storeUrl = url.searchParams.get('store_url')
-
-      if (!storeUrl) {
-        return new Response(
-          JSON.stringify({ error: 'store_url parameter is required' }),
-          { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      // Validate store URL
-      const validation = validateStoreUrl(storeUrl)
-      if (!validation.valid) {
-        return new Response(
-          JSON.stringify({ error: validation.error }),
-          { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      // Get user from authorization header
-      const authHeader = req.headers.get('authorization')
-      if (!authHeader) {
-        return new Response(
-          JSON.stringify({ error: 'No authorization header' }),
-          { status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      const token = authHeader.replace('Bearer ', '')
-      const supabase = createClient(supabaseUrl, supabaseKey)
-      const { data: { user }, error: userError } = await supabase.auth.getUser(token)
-
-      if (userError || !user) {
-        return new Response(
-          JSON.stringify({ error: 'Invalid token' }),
-          { status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      // Generate state for CSRF protection
-      const state = crypto.randomUUID()
-
-      // Create Supabase admin client for storing state (requires service role)
-      const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey)
-
-      // Store state in database
-      const { error: stateError } = await supabaseAdmin
-        .from('oauth_states')
-        .insert({
-          state,
-          user_id: user.id,
-          platform: 'woocommerce',
-          shopname: validation.normalized,
-          expires_at: new Date(Date.now() + 15 * 60 * 1000).toISOString()
-        })
-
-      if (stateError) {
-        console.error('[WooCommerce] Error storing state:', stateError)
-        return new Response(
-          JSON.stringify({ error: 'Failed to initiate OAuth flow' }),
-          { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-        )
-      }
-
-      // Build WooCommerce OAuth authorization URL
-      // IMPORTANT: Callback URL must NOT contain query parameters (WooCommerce requirement)
-      // We'll detect callbacks by checking POST method + WooCommerce OAuth parameters
-      const callbackUrl = `https://${url.host}${url.pathname}`
-      const appName = 'ShopCall.ai'
-      const returnUrl = `${frontendUrl}/webshops?wc_connected=true`
-
-      const authUrl = new URL(`${validation.normalized}/wc-auth/v1/authorize`)
-      authUrl.searchParams.set('app_name', appName)
-      authUrl.searchParams.set('scope', 'read')
-      authUrl.searchParams.set('user_id', user.id)
-      authUrl.searchParams.set('return_url', returnUrl)
-      authUrl.searchParams.set('callback_url', callbackUrl)
-
-      console.log(`[WooCommerce] OAuth initiated for ${validation.normalized}`)
-      console.log(`[WooCommerce] Callback URL: ${callbackUrl}`)
-
-      return new Response(
-        JSON.stringify({
-          success: true,
-          authUrl: authUrl.toString(),
-          message: 'Redirect to WooCommerce for authorization'
-        }),
-        { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
-      )
-    }
 
 
     // Handle manual API key connection
     // Handle manual API key connection
     if (action === 'connect_manual') {
     if (action === 'connect_manual') {