Browse Source

ref: remove Vercel dependencies and backend directory #4

- Removed entire shopcall.ai-backend-main directory
- Removed vercel.json from frontend
- Updated CLAUDE.md to reflect Supabase Edge Functions architecture
- Updated DEPLOYMENT_GUIDE.md to remove Vercel-specific instructions
- Project now uses static frontend + Supabase Edge Functions only
Claude 5 months ago
parent
commit
2fe5df5003

+ 12 - 0
.mcp-gogs.json

@@ -0,0 +1,12 @@
+{
+  "mcpServers": {
+    "gogs": {
+      "type": "stdio",
+      "command": "node",
+      "args": [
+        "/home/claude/gogs-mcp/dist/index.js"
+      ],
+      "env": {}
+    }
+  }
+}

+ 56 - 111
CLAUDE.md

@@ -4,10 +4,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
 
 ## Project Overview
 
-ShopCall.ai is a dual-repository application for AI-powered calling system integrated with e-commerce platforms. The project consists of:
+ShopCall.ai is an AI-powered calling system integrated with e-commerce platforms. The project consists of:
 
 - **shopcall.ai-main**: React/Vite frontend application with TypeScript
-- **shopcall.ai-backend-main**: Express.js backend API
+- **supabase**: Supabase Edge Functions for backend API
 
 ## Repository Structure
 
@@ -23,11 +23,8 @@ ShopCall.ai is a dual-repository application for AI-powered calling system integ
 │   │   ├── lib/               # Utility functions
 │   │   └── App.tsx            # Main application with routing
 │   └── package.json
-└── shopcall.ai-backend-main/  # Backend API
-    ├── api/
-    │   └── index.js           # Main Express server (950 lines)
-    ├── public/                # Static files
-    └── package.json
+└── supabase/                   # Supabase Edge Functions
+    └── functions/              # Backend API functions
 ```
 
 ## Development Commands
@@ -43,11 +40,13 @@ npm run lint             # Run ESLint
 npm run preview          # Preview production build
 ```
 
-### Backend (shopcall.ai-backend-main)
+### Supabase Edge Functions
 ```bash
-cd shopcall.ai-backend-main
-npm install              # Install dependencies
-npm start                # Start server (node api/index)
+# Deploy Edge Functions
+supabase functions deploy <function-name>
+
+# Test locally
+supabase functions serve
 ```
 
 ## Technology Stack
@@ -63,11 +62,9 @@ npm start                # Start server (node api/index)
 - **Theme**: next-themes for dark mode support
 
 ### Backend
-- **Framework**: Express.js v5
-- **Language**: JavaScript (Node.js)
+- **Platform**: Supabase Edge Functions (Deno)
 - **Database**: Supabase (PostgreSQL with auth)
-- **Email**: Nodemailer (Gmail)
-- **CORS**: Enabled for cross-origin requests
+- **Authentication**: Supabase Auth
 
 ## Architecture
 
@@ -77,7 +74,7 @@ npm start                # Start server (node api/index)
 - AuthContext provider (`src/components/context/AuthContext.tsx`) manages global auth state
 - Token stored in localStorage as `session_data`
 - PrivateRoute component protects authenticated routes
-- Auth validation checks session token via `/auth/check` endpoint
+- Auth validation via Supabase client
 
 **Routing Structure**:
 ```
@@ -103,48 +100,17 @@ npm start                # Start server (node api/index)
 
 ### Backend Architecture
 
-**Single Server File**: All API logic is in `api/index.js` (~950 lines)
-
-**Core Middleware**:
-- CORS enabled globally
-- Raw body parser for `/gdpr` webhooks
-- JSON/urlencoded parsers for other routes
-- `securedSession` middleware validates Supabase auth tokens
-
-**Authentication Endpoints**:
-- `POST /auth/signup` - Step 1: Store user data, generate and send OTP
-- `POST /auth/signup/verify` - Step 2: Verify OTP and create Supabase user
-- `POST /auth/signup/resend-otp` - Resend OTP email
-- `POST /auth/login` - Email/password login via Supabase
-- `POST /auth/logout` - Sign out
-- `GET /auth/check` - Validate bearer token
-
-**E-commerce Integration Endpoints**:
-- `GET /auth/shopify` - Initialize Shopify OAuth flow
-- `GET /auth/shopify/callback` - Handle Shopify OAuth callback
-- `GET /auth/woocommerce` - Initialize WooCommerce OAuth (requires auth)
-- `POST /auth/woocommerce/callback` - Handle WooCommerce credentials
-
-**Webhook Endpoints** (GDPR compliance):
-- `POST /gdpr/customers-data-request` - Customer data request
-- `POST /gdpr/customers-redact` - Customer redaction
-- `POST /gdpr/shop-redact` - Shop redaction
-- HMAC verification via `verifyWebhook` middleware
-
-**Other Endpoints**:
-- `GET /health` - Health check
-
-**Key Functions**:
-- `generateOTP()` - Creates 6-digit OTP
-- `sendOTPEmail(email, otp, userName)` - Sends styled verification email
-- `normalizeShopUrl(shop)` - Cleans Shopify URLs
-- `isValidShopUrl(shop)` - Validates Shopify domain format
-- `processWebhook(req, res, topic)` - Async webhook processing
-
-**State Management**:
-- `pendingSignups` Map - Temporary storage for unverified signups (15 min TTL)
-- `nonceStore` Map - OAuth state validation (10 min TTL)
-- ⚠️ Production should use Redis/database instead of in-memory Maps
+**Supabase Edge Functions**: Backend logic implemented as serverless Edge Functions
+
+**Authentication**:
+- Handled by Supabase Auth
+- User management and session handling via Supabase client
+
+**E-commerce Integrations**:
+- Shopify OAuth flow
+- WooCommerce OAuth flow
+- ShopRenter integration
+- Store credentials in Supabase `stores` table
 
 ### Database Schema (Supabase)
 
@@ -164,85 +130,64 @@ npm start                # Start server (node api/index)
 
 ## Environment Configuration
 
-### Backend `.env` (shopcall.ai-backend-main)
+### Frontend `.env` (shopcall.ai-main)
 ```bash
 # Supabase Configuration
-SUPABASE_URL=https://ztklqodcdjeqpsvhlpud.supabase.co
-SUPABASE_ANON_KEY=<anon_key>
+VITE_SUPABASE_URL=https://ztklqodcdjeqpsvhlpud.supabase.co
+VITE_SUPABASE_ANON_KEY=<anon_key>
 
-# URL Configuration
-BACKEND_URL=https://shopcall-ai-backend.vercel.app  # Backend API base URL
-FRONTEND_URL=https://shopcall.ai                     # Frontend application URL
+# Backend API Base URL (Supabase Edge Functions)
+VITE_API_URL=https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1
 
+# Frontend URL (for OAuth callbacks)
+VITE_FRONTEND_URL=https://shopcall.ai
+```
+
+### Supabase Edge Functions `.env`
+```bash
 # OAuth Configuration
 SHOPIFY_API_KEY=<shopify_api_key>
 SHOPIFY_API_SECRET=<shopify_api_secret>
 SHOPRENTER_CLIENT_ID=<shoprenter_client_id>
 SHOPRENTER_CLIENT_SECRET=<shoprenter_client_secret>
 
-# Email Configuration
+# Email Configuration (if needed)
 EMAIL_USER=<gmail_address>
 EMAIL_PASSWORD=<gmail_app_password>
 
-# Environment
-NODE_ENV=production|development
+# Frontend URL
+FRONTEND_URL=https://shopcall.ai
 ```
 
-### Frontend `.env` (shopcall.ai-main)
-```bash
-# Backend API Base URL
-VITE_API_URL=https://ztklqodcdjeqpsvhlpud.supabase.co/functions/v1
-
-# Frontend URL (for OAuth callbacks)
-VITE_FRONTEND_URL=https://shopcall.ai
-```
-
-**Note**: See `.env.example` files in both repositories for complete configuration templates.
-
 ## Deployment
 
-Both applications deploy to Vercel:
-
-**Backend** (`vercel.json`):
-```json
-{
-  "version": 2,
-  "rewrites": [{ "source": "/(.*)", "destination": "/api" }]
-}
-```
-- All routes proxy to `/api/index.js`
-- Deployed at: `https://shopcall-ai-backend.vercel.app` (configure via `BACKEND_URL` env var)
-- **Important**: Set `BACKEND_URL` and `FRONTEND_URL` environment variables in Vercel project settings
-
-**Frontend** (`vercel.json`):
-```json
-{
-  "rewrites": [{ "source": "/(.*)", "destination": "/" }]
-}
-```
+### Frontend
+- **Build**: `npm run build` generates static files in `dist/`
+- **Deployment**: Can be deployed to any static hosting service (Netlify, Vercel, GitHub Pages, etc.)
 - SPA routing handled by React Router
-- Deployed at: `https://shopcall.ai` (configure via `VITE_FRONTEND_URL` env var)
-- **Important**: Set `VITE_API_URL` and `VITE_FRONTEND_URL` environment variables in Vercel project settings
+
+### Supabase Edge Functions
+- **Deployment**: Use `supabase functions deploy` command
+- Edge Functions are deployed to Supabase infrastructure
+- Automatically scaled and managed by Supabase
 
 ## Development Workflow
 
-When making changes across both repositories:
+When making changes:
 
-1. **Backend Changes**: Modify `shopcall.ai-backend-main/api/index.js`
-2. **Frontend Changes**: Work in `shopcall.ai-main/src/`
-3. **Auth Flow Changes**: Update both AuthContext and backend `/auth/*` endpoints
+1. **Frontend Changes**: Work in `shopcall.ai-main/src/`
+2. **Backend Changes**: Modify Supabase Edge Functions in `supabase/functions/`
+3. **Auth Flow Changes**: Update AuthContext and Supabase Auth configuration
 4. **New Protected Routes**: Add to App.tsx inside `<PrivateRoute>` wrapper
-5. **New API Endpoints**: Add to `api/index.js` with `securedSession` if auth required
+5. **New API Endpoints**: Create new Edge Functions in `supabase/functions/`
 
 ## Important Notes
 
-- All backend logic is in a single file (`api/index.js`)
-- In-memory stores (`pendingSignups`, `nonceStore`) will reset on serverless function cold starts
-- **URLs are configurable via environment variables** (`BACKEND_URL`, `FRONTEND_URL`, `VITE_API_URL`, `VITE_FRONTEND_URL`)
-- Backend and frontend URLs have sensible defaults for development and production
-- Supabase handles user authentication and data storage
-- Email verification required for signup (OTP sent via Gmail)
-- Shopify/WooCommerce/ShopRenter OAuth flows store credentials in Supabase `stores` table
+- Backend is implemented as Supabase Edge Functions (serverless)
+- Supabase handles user authentication, data storage, and API endpoints
+- Frontend is a static React application that can be hosted anywhere
+- All API calls go through Supabase Edge Functions
+- OAuth flows store credentials in Supabase `stores` table
 
 ## Path Aliases
 

+ 41 - 64
DEPLOYMENT_GUIDE.md

@@ -1,26 +1,19 @@
 # ShopCall.ai Deployment Guide
 
-This guide explains how to deploy ShopCall.ai after the migration from Vercel to Supabase Edge Functions with static hosting.
+This guide explains how to deploy ShopCall.ai with static hosting and Supabase Edge Functions.
 
 ## Architecture Overview
 
-### New Architecture
-- **Frontend**: Static React build (Vite) - Can be hosted on any static hosting provider (Apache, Nginx, Cloudflare Pages, etc.)
+### Current Architecture
+- **Frontend**: Static React build (Vite) - Can be hosted on any static hosting provider
 - **Backend**: Supabase Edge Functions (Deno runtime)
 - **Database**: Supabase PostgreSQL
 - **Authentication**: Supabase Auth
 
-### Key Changes from Previous Architecture
-- ✅ Replaced Vercel serverless functions with Supabase Edge Functions
-- ✅ Replaced in-memory stores with database tables (`pending_signups`, `oauth_nonces`)
-- ✅ Migrated from Nodemailer to Resend API for email sending
-- ✅ Environment variable-based configuration for easy deployment
-- ✅ Static hosting compatible frontend
-
 ## Prerequisites
 
 1. Supabase account and project
-2. Web hosting with Apache or Nginx (for static files)
+2. Static web hosting (Apache, Nginx, Cloudflare Pages, Netlify, etc.)
 3. Resend API key for email sending (or alternative email service)
 4. Shopify API credentials (if using Shopify integration)
 5. Node.js and npm installed locally for building
@@ -35,6 +28,9 @@ Stores temporary signup data with OTP for email verification (15-minute expirati
 ### 2. oauth_nonces
 Stores OAuth state/nonce values for secure OAuth flows (10-minute expiration).
 
+### 3. stores
+Stores e-commerce platform credentials and configuration.
+
 ## Edge Functions Deployment
 
 ### Deployed Edge Functions
@@ -62,7 +58,7 @@ Stores OAuth state/nonce values for secure OAuth flows (10-minute expiration).
 
 ### Environment Variables for Edge Functions
 
-Configure these in your Supabase project settings under Edge Functions:
+Configure these in your Supabase project settings:
 
 ```bash
 # Supabase (automatically available)
@@ -91,6 +87,10 @@ EDGE_FUNCTION_BASE_URL=https://YOUR_PROJECT.supabase.co/functions/v1
 Create or update `.env` file in `shopcall.ai-main/`:
 
 ```bash
+# Supabase Configuration
+VITE_SUPABASE_URL=https://YOUR_PROJECT.supabase.co
+VITE_SUPABASE_ANON_KEY=your_anon_key
+
 # Backend API Base URL (Supabase Edge Functions)
 VITE_API_URL=https://YOUR_PROJECT.supabase.co/functions/v1
 
@@ -113,8 +113,18 @@ This creates a `dist/` directory with your static files.
 #### Option A: Apache Hosting
 
 1. Upload the contents of `dist/` to your web server (e.g., `/var/www/html/`)
-2. Ensure `.htaccess` file is in the root directory (already created in `public/`)
-3. Make sure `mod_rewrite` is enabled:
+2. Create `.htaccess` file in the root directory for SPA routing:
+   ```apache
+   <IfModule mod_rewrite.c>
+     RewriteEngine On
+     RewriteBase /
+     RewriteRule ^index\.html$ - [L]
+     RewriteCond %{REQUEST_FILENAME} !-f
+     RewriteCond %{REQUEST_FILENAME} !-d
+     RewriteRule . /index.html [L]
+   </IfModule>
+   ```
+3. Enable `mod_rewrite`:
    ```bash
    sudo a2enmod rewrite
    sudo systemctl restart apache2
@@ -122,14 +132,21 @@ This creates a `dist/` directory with your static files.
 
 #### Option B: Nginx Hosting
 
-1. Upload the contents of `dist/` to your web server (e.g., `/var/www/shopcall.ai/dist/`)
-2. Copy `nginx.conf.example` to your nginx sites-available directory:
-   ```bash
-   sudo cp nginx.conf.example /etc/nginx/sites-available/shopcall.ai
-   sudo ln -s /etc/nginx/sites-available/shopcall.ai /etc/nginx/sites-enabled/
+1. Upload the contents of `dist/` to your web server (e.g., `/var/www/shopcall.ai/`)
+2. Configure nginx:
+   ```nginx
+   server {
+       listen 80;
+       server_name yourdomain.com;
+       root /var/www/shopcall.ai;
+       index index.html;
+
+       location / {
+           try_files $uri $uri/ /index.html;
+       }
+   }
    ```
-3. Update the configuration with your domain and SSL certificates
-4. Test and reload nginx:
+3. Test and reload:
    ```bash
    sudo nginx -t
    sudo systemctl reload nginx
@@ -138,8 +155,8 @@ This creates a `dist/` directory with your static files.
 #### Option C: Cloudflare Pages / Netlify / Similar
 
 1. Connect your Git repository
-2. Set build command: `npm run build`
-3. Set publish directory: `dist`
+2. Set build command: `cd shopcall.ai-main && npm run build`
+3. Set publish directory: `shopcall.ai-main/dist`
 4. Configure environment variables in the platform's dashboard
 
 ## Email Service Setup (Resend)
@@ -150,7 +167,7 @@ This creates a `dist/` directory with your static files.
    ```bash
    supabase secrets set RESEND_API_KEY=your_api_key
    ```
-4. Verify your sending domain in Resend (optional but recommended for production)
+4. Verify your sending domain in Resend (recommended for production)
 
 ## OAuth Configuration
 
@@ -230,46 +247,6 @@ curl -X POST https://YOUR_PROJECT.supabase.co/functions/v1/auth/login \
    - `delete_expired_pending_signups()`
    - `delete_expired_oauth_nonces()`
 
-## Vercel Backend Deployment (Alternative)
-
-If using the Vercel backend (`shopcall.ai-backend-main/`), configure these environment variables:
-
-```bash
-# Supabase Configuration
-SUPABASE_URL=https://YOUR_PROJECT.supabase.co
-SUPABASE_ANON_KEY=your_anon_key
-
-# URL Configuration
-BACKEND_URL=https://shopcall-ai-backend.vercel.app  # Your backend deployment URL
-FRONTEND_URL=https://shopcall.ai                     # Your frontend URL
-
-# OAuth Configuration
-SHOPIFY_API_KEY=your_shopify_api_key
-SHOPIFY_API_SECRET=your_shopify_api_secret
-SHOPRENTER_CLIENT_ID=your_shoprenter_client_id
-SHOPRENTER_CLIENT_SECRET=your_shoprenter_client_secret
-
-# Email Configuration
-EMAIL_USER=your_gmail_address@gmail.com
-EMAIL_PASSWORD=your_gmail_app_password
-
-# Environment
-NODE_ENV=production
-```
-
-**Important**: Set `BACKEND_URL` and `FRONTEND_URL` environment variables to match your actual deployment URLs. This allows for flexible deployment across different environments.
-
-## Rollback Plan
-
-If you need to rollback to the old Vercel deployment:
-
-1. Update frontend `.env` to point back to Vercel backend:
-   ```bash
-   VITE_API_URL=https://shopcall-ai-backend.vercel.app
-   ```
-2. Rebuild and redeploy frontend
-3. The old backend code is still available in `shopcall.ai-backend-main/`
-
 ## Security Considerations
 
 1. **Environment Variables**: Never commit `.env` files to version control

+ 0 - 28
shopcall.ai-backend-main/.env.example

@@ -1,28 +0,0 @@
-# Supabase Configuration
-SUPABASE_URL=https://YOUR_PROJECT.supabase.co
-SUPABASE_ANON_KEY=your_supabase_anon_key_here
-
-# Backend URL (this backend's public URL)
-# For production: https://your-backend.vercel.app or https://api.yourdomain.com
-# For local dev: http://localhost:3000
-BACKEND_URL=https://shopcall-ai-backend.vercel.app
-
-# Frontend URL (where users access the application)
-# For production: https://yourdomain.com
-# For local dev: http://localhost:8080
-FRONTEND_URL=https://shopcall.ai
-
-# Shopify OAuth Configuration
-SHOPIFY_API_KEY=your_shopify_api_key
-SHOPIFY_API_SECRET=your_shopify_api_secret
-
-# ShopRenter OAuth Configuration
-SHOPRENTER_CLIENT_ID=your_shoprenter_client_id
-SHOPRENTER_CLIENT_SECRET=your_shoprenter_client_secret
-
-# Email Configuration (for OTP emails)
-EMAIL_USER=your_gmail_address@gmail.com
-EMAIL_PASSWORD=your_gmail_app_password
-
-# Environment
-NODE_ENV=production

+ 0 - 36
shopcall.ai-backend-main/.gitignore

@@ -1,36 +0,0 @@
-# Dependencies
-node_modules/
-package-lock.json
-
-# Environment variables
-.env
-.env.local
-.env.development
-.env.production
-
-# Logs
-logs/
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-# OS files
-.DS_Store
-Thumbs.db
-
-# IDE files
-.vscode/
-.idea/
-*.swp
-*.swo
-*~
-
-# Test files
-test-supabase.js
-check-schema.js
-
-# Vercel
-.vercel

+ 0 - 1754
shopcall.ai-backend-main/api/index.js

@@ -1,1754 +0,0 @@
-const express = require('express');
-const crypto = require('crypto');
-const axios = require('axios');
-const { createClient } = require('@supabase/supabase-js');
-const path = require('path');
-const nodemailer = require('nodemailer');
-const app = express();
-require('dotenv').config();
-const cors = require('cors');
-const querystring = require('querystring');
-
-app.use(cors());
-
-// Add raw body parser for webhooks BEFORE any other middleware
-app.use('/gdpr', express.raw({ type: '*/*' }));
-
-// Regular middleware for other routes
-app.use(express.json());
-app.use(express.urlencoded({ extended: true }));
-app.use(express.static('public'));
-
-// Supabase configuration
-const supabaseUrl = process.env.SUPABASE_URL;
-const supabaseKey = process.env.SUPABASE_ANON_KEY;
-
-if (!supabaseUrl || !supabaseKey) {
-  console.error('Missing Supabase configuration. Please set SUPABASE_URL and SUPABASE_ANON_KEY in your .env file');
-}
-
-const supabase = createClient(supabaseUrl, supabaseKey);
-
-// Email transporter configuration
-const emailTransporter = nodemailer.createTransport({
-  service: 'gmail', // You can change this to your email provider
-  auth: {
-    user: process.env.EMAIL_USER || 'srwusc123@gmail.com',
-    pass: process.env.EMAIL_PASSWORD || 'sach zpxx dqec hhtq'
-  }
-});
-
-// Helper function to generate OTP
-function generateOTP() {
-  return Math.floor(100000 + Math.random() * 900000).toString(); // 6-digit OTP
-}
-
-// Helper function to send OTP email
-async function sendOTPEmail(email, otp, userName) {
-  const mailOptions = {
-    from: process.env.EMAIL_USER || 'srwusc123@gmail.com',
-    to: email,
-    subject: 'Verify Your Account - ShopCall.ai',
-    html: `
-      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
-        <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; border-radius: 10px; text-align: center; margin-bottom: 30px;">
-          <h1 style="color: white; margin: 0; font-size: 2rem;">SHOPCALL.AI</h1>
-          <p style="color: white; margin: 10px 0 0 0; opacity: 0.9;">Account Verification</p>
-        </div>
-        
-        <div style="background: #f8f9fa; padding: 30px; border-radius: 10px; text-align: center;">
-          <h2 style="color: #333; margin: 0 0 20px 0;">Hello ${userName}!</h2>
-          <p style="color: #666; margin-bottom: 30px; font-size: 16px;">
-            Please use the following verification code to complete your account setup:
-          </p>
-          
-          <div style="background: white; border: 2px solid #667eea; border-radius: 10px; padding: 20px; margin: 20px 0; display: inline-block;">
-            <div style="font-size: 32px; font-weight: bold; color: #667eea; letter-spacing: 5px;">${otp}</div>
-          </div>
-          
-          <p style="color: #666; font-size: 14px; margin-top: 30px;">
-            This code will expire in 15 minutes. If you didn't request this verification, please ignore this email.
-          </p>
-        </div>
-        
-        <div style="text-align: center; margin-top: 30px; color: #999; font-size: 12px;">
-          <p>© 2024 AI Caller. All rights reserved.</p>
-        </div>
-      </div>
-    `
-  };
-
-  try {
-    await emailTransporter.sendMail(mailOptions);
-    return { success: true };
-  } catch (error) {
-    console.error('Email sending error:', error);
-    return { success: false, error: error.message };
-  }
-}
-
-// Environment URLs
-const BACKEND_URL = process.env.BACKEND_URL || (process.env.NODE_ENV === 'production'
-  ? 'https://shopcall-ai-backend.vercel.app'
-  : 'http://localhost:3000');
-
-const FRONTEND_URL = process.env.FRONTEND_URL || (process.env.NODE_ENV === 'production'
-  ? 'https://shopcall.ai'
-  : 'http://localhost:8080');
-
-// Shopify Configuration
-const config = {
-  apiKey: process.env.SHOPIFY_API_KEY,
-  apiSecret: process.env.SHOPIFY_API_SECRET,
-  scopes: 'read_products,write_products,read_orders,write_orders,read_customers,write_customers',
-  redirectUri: `${BACKEND_URL}/auth/shopify/callback`
-};
-
-// ShopRenter Configuration
-const shoprenterConfig = {
-  clientId: process.env.SHOPRENTER_CLIENT_ID,
-  clientSecret: process.env.SHOPRENTER_CLIENT_SECRET,
-  scopes: 'product:read product:write customer:read customer:write order:read order:write webhook:read webhook:write',
-  redirectUri: `${BACKEND_URL}/auth/shoprenter/callback`,
-  entryPoint: `${BACKEND_URL}/auth/shoprenter`,
-  uninstallUri: `${BACKEND_URL}/auth/shoprenter/uninstall`
-};
-
-// Store nonces temporarily (use Redis or database in production)
-const nonceStore = new Map();
-
-// Helper function to generate nonce
-function generateNonce() {
-  return 'nonce_' + crypto.randomBytes(16).toString('hex');
-}
-
-// Helper function to normalize shop URL
-function normalizeShopUrl(shop) {
-  if (!shop) return null;
-
-  // Remove protocol if present
-  shop = shop.replace(/^https?:\/\//, '');
-
-  // Remove trailing slash if present
-  shop = shop.replace(/\/$/, '');
-
-  // Remove any path after the domain
-  shop = shop.split('/')[0];
-
-  return shop;
-}
-
-// Helper function to validate shop URL
-function isValidShopUrl(shop) {
-  if (!shop) return false;
-
-  // Normalize the shop URL
-  const normalizedShop = normalizeShopUrl(shop);
-
-  // Check if it's a valid Shopify shop URL
-  // This regex allows for:
-  // - Letters, numbers, and hyphens in the subdomain
-  // - .myshopify.com domain
-  // - Optional .com, .co.uk, etc. TLDs
-  const shopRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]*\.myshopify\.com$/;
-
-  return shopRegex.test(normalizedShop);
-}
-
-// ShopRenter HMAC validation function
-function validateShopRenterHMAC(query, clientSecret) {
-  const { hmac, ...params } = query;
-
-  if (!hmac) {
-    return { valid: false, error: 'Missing HMAC parameter' };
-  }
-
-  try {
-    // Sort parameters alphabetically and create query string
-    const sortedParams = Object.keys(params)
-      .sort()
-      .map(key => `${key}=${params[key]}`)
-      .join('&');
-
-    // Calculate HMAC using SHA256
-    const calculatedHmac = crypto
-      .createHmac('sha256', clientSecret)
-      .update(sortedParams)
-      .digest('hex');
-
-    // Compare HMACs using timing-safe comparison
-    const hmacValid = crypto.timingSafeEqual(
-      Buffer.from(calculatedHmac),
-      Buffer.from(hmac)
-    );
-
-    if (!hmacValid) {
-      return { valid: false, error: 'Invalid HMAC signature' };
-    }
-
-    return { valid: true };
-  } catch (error) {
-    console.error('HMAC validation error:', error);
-    return { valid: false, error: 'HMAC validation failed' };
-  }
-}
-
-// Middleware to verify ShopRenter requests
-const verifyShopRenterRequest = (req, res, next) => {
-  const validation = validateShopRenterHMAC(req.query, shoprenterConfig.clientSecret);
-
-  if (!validation.valid) {
-    console.error('ShopRenter request validation failed:', validation.error);
-    return res.status(403).json({
-      error: 'Invalid request signature',
-      details: validation.error
-    });
-  }
-
-  next();
-};
-
-// Secured Session Middleware
-const securedSession = async (req, res, next) => {
-  try {
-    const authHeader = req.headers.authorization;
-    if (!authHeader) {
-      return res.status(401).json({ error: 'No authorization header' });
-    }
-
-    const token = authHeader.replace('Bearer ', '');
-    const { data: { user }, error: authError } = await supabase.auth.getUser(token);
-
-    if (authError || !user) {
-      return res.status(401).json({ error: 'Invalid token' });
-    }
-
-    // Attach user to request object for use in route handlers
-    req.user = user;
-    next();
-  } catch (error) {
-    console.error('Session verification error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-};
-
-// Authentication Routes
-
-// Serve login page
-app.get('/login', (req, res) => {
-  res.sendFile(path.join(__dirname, 'public', 'login.html'));
-});
-
-// Serve signup page
-app.get('/signup', (req, res) => {
-  res.sendFile(path.join(__dirname, 'public', 'signup.html'));
-});
-
-// Store pending signups temporarily (use Redis or database in production)
-const pendingSignups = new Map();
-
-// Signup endpoint - Step 1: Store user data and send OTP
-app.post('/auth/signup', async (req, res) => {
-  try {
-    const { full_name, company_name, user_name, email, password } = req.body;
-
-    // Validate required fields
-    if (!full_name || !company_name || !user_name || !email || !password) {
-      return res.status(400).json({ error: 'All fields are required' });
-    }
-
-    // Generate OTP
-    const otp = generateOTP();
-
-    // Store pending signup data with OTP
-    const signupId = crypto.randomBytes(16).toString('hex');
-    pendingSignups.set(signupId, {
-      full_name,
-      company_name,
-      user_name,
-      email,
-      password,
-      otp,
-      timestamp: Date.now(),
-      expiresAt: Date.now() + (15 * 60 * 1000) // 15 minutes
-    });
-
-    // Send OTP to email for verification
-    const emailResult = await sendOTPEmail(email, otp, full_name);
-
-    if (!emailResult.success) {
-      pendingSignups.delete(signupId);
-      return res.status(500).json({ error: 'Failed to send verification email. Please try again.' });
-    }
-
-    res.json({
-      success: true,
-      message: 'OTP sent to your email. Please verify to complete registration.',
-      signupId: signupId,
-      requiresOtpVerification: true
-    });
-
-  } catch (error) {
-    console.error('Signup error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Signup verification endpoint - Step 2: Verify OTP and create account
-app.post('/auth/signup/verify', async (req, res) => {
-  try {
-    const { signupId, otp } = req.body;
-
-    if (!signupId || !otp) {
-      return res.status(400).json({ error: 'Signup ID and OTP are required' });
-    }
-
-    // Get pending signup data
-    const pendingData = pendingSignups.get(signupId);
-    if (!pendingData) {
-      return res.status(400).json({ error: 'Invalid or expired signup session' });
-    }
-
-    // Check expiration
-    if (Date.now() > pendingData.expiresAt) {
-      pendingSignups.delete(signupId);
-      return res.status(400).json({ error: 'Signup session expired. Please start again.' });
-    }
-
-    // Verify OTP (compare with stored OTP)
-    if (otp !== pendingData.otp) {
-      return res.status(400).json({ error: 'Invalid OTP code' });
-    }
-
-    // OTP verified, now create the user account
-    const { data, error } = await supabase.auth.signUp({
-      email: pendingData.email,
-      password: pendingData.password,
-      options: {
-        data: {
-          is_verified: true,
-          full_name: pendingData.full_name,
-          company_name: pendingData.company_name,
-          user_name: pendingData.user_name
-        }
-      }
-    });
-
-    if (error) {
-      return res.status(400).json({ error: error.message });
-    }
-
-    // Clean up pending signup
-    pendingSignups.delete(signupId);
-
-    res.json({
-      success: true,
-      message: 'Account created successfully! You can now sign in.',
-      user: data.user
-    });
-
-  } catch (error) {
-    console.error('Signup verification error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Login endpoint - Simple email/password authentication
-app.post('/auth/login', async (req, res) => {
-  try {
-    const { email, password } = req.body;
-
-    if (!email || !password) {
-      return res.status(400).json({ error: 'Email and password are required' });
-    }
-
-    // Sign in with email and password
-    const { data, error } = await supabase.auth.signInWithPassword({
-      email: email,
-      password: password
-    });
-
-    if (error) {
-      console.log(error);
-      return res.status(400).json({ error: 'Invalid email or password' });
-    }
-
-    res.json({
-      success: true,
-      message: 'Login successful',
-      user: data.user,
-      session: data.session
-    });
-
-  } catch (error) {
-    console.error('Login error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Logout endpoint
-app.post('/auth/logout', async (req, res) => {
-  try {
-    const { error } = await supabase.auth.signOut();
-
-    if (error) {
-      return res.status(400).json({ error: error.message });
-    }
-
-    res.json({
-      success: true,
-      message: 'Logout successful'
-    });
-
-  } catch (error) {
-    console.error('Logout error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Get current user
-app.get('/auth/check', async (req, res) => {
-  try {
-    const authHeader = req.headers.authorization;
-    if (!authHeader) {
-      return res.status(401).json({ error: 'No authorization header' });
-    }
-
-    const token = authHeader.replace('Bearer ', '');
-    const { data: { user }, error } = await supabase.auth.getUser(token);
-
-    if (error || !user) {
-      return res.status(401).json({ error: 'Invalid token' });
-    }
-
-    res.json({
-      success: true,
-      user: user
-    });
-
-  } catch (error) {
-    console.error('Get user error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-
-// Resend OTP for signup verification
-app.post('/auth/signup/resend-otp', async (req, res) => {
-  try {
-    const { signupId } = req.body;
-
-    if (!signupId) {
-      return res.status(400).json({ error: 'Signup ID is required' });
-    }
-
-    // Get pending signup data
-    const pendingData = pendingSignups.get(signupId);
-    if (!pendingData) {
-      return res.status(400).json({ error: 'Invalid or expired signup session' });
-    }
-
-    // Check if still valid
-    if (Date.now() > pendingData.expiresAt) {
-      pendingSignups.delete(signupId);
-      return res.status(400).json({ error: 'Signup session expired. Please start again.' });
-    }
-
-    // Generate new OTP
-    const newOtp = generateOTP();
-
-    // Update the stored OTP
-    pendingData.otp = newOtp;
-    pendingSignups.set(signupId, pendingData);
-
-    // Send new OTP
-    const emailResult = await sendOTPEmail(pendingData.email, newOtp, pendingData.full_name);
-
-    if (!emailResult.success) {
-      return res.status(500).json({ error: 'Failed to send verification email' });
-    }
-
-    res.json({
-      success: true,
-      message: 'New OTP sent to your email'
-    });
-
-  } catch (error) {
-    console.error('Resend OTP error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Health check
-app.get('/health', (req, res) => {
-  res.json({
-    status: 'OK',
-    environment: process.env.NODE_ENV || 'development',
-    redirectUri: config.redirectUri
-  });
-});
-
-// Webshop Authentication Routes
-
-// Route 1: Initialize OAuth (redirect merchant to Shopify)
-app.get('/auth/shopify', (req, res) => {
-  const { shop } = req.query;
-
-  // Validate shop parameter
-  if (!shop) {
-    console.error('Missing shop parameter');
-    return res.status(400).json({
-      error: 'Shop parameter is required',
-      details: 'Please provide a valid Shopify shop URL'
-    });
-  }
-
-  // Normalize and validate shop URL
-  const normalizedShop = normalizeShopUrl(shop);
-  if (!isValidShopUrl(normalizedShop)) {
-    console.error(`Invalid shop URL: ${shop}`);
-    return res.status(400).json({
-      error: 'Invalid shop URL',
-      details: 'Please provide a valid Shopify shop URL (e.g., your-store.myshopify.com)'
-    });
-  }
-
-  // Generate and store nonce with user ID
-  const nonce = generateNonce();
-  nonceStore.set(nonce, {
-    shop: normalizedShop,
-    userId: req?.user?.id, // Store the authenticated user's ID
-    timestamp: Date.now(),
-    expiresAt: Date.now() + (10 * 60 * 1000) // 10 minutes
-  });
-
-  // Build authorization URL
-  const authUrl = `https://${normalizedShop}/admin/oauth/authorize?client_id=${config.apiKey}&scope=${config.scopes}&redirect_uri=${config.redirectUri}&state=${nonce}&grant_options[]=per-user`;
-
-  console.log(`Initiating OAuth flow for shop: ${normalizedShop}`);
-  res.redirect(authUrl);
-});
-
-
-app.get('/auth/woocommerce', securedSession, (req, res) => {
-  const { shop_url, phone_number, package, platform } = req.query;
-  const store_url = shop_url;
-  const endpoint = '/wc-auth/v1/authorize';
-  const params = {
-    app_name: 'ShopCall.ai',
-    scope: 'read_write',
-    user_id: req.user.id,
-    return_url: `${FRONTEND_URL}/dashboard`,
-    callback_url: `${BACKEND_URL}/auth/woocommerce/callback?platform=${platform}&shop_url=${shop_url}&phone_number=${phone_number}&package=${package}`
-  };
-  const query_string = querystring.stringify(params).replace(/%20/g, '+');
-  const redirect_url = store_url + endpoint + '?' + query_string;
-  console.log("redirect_url", redirect_url);
-  res.redirect(redirect_url);
-})
-
-// Route 2: Handle Woocommerce Callback
-app.post('/auth/woocommerce/callback', async (req, res) => {
-  const { platform, shop_url, phone_number, package } = req.query;
-  const { key_id, user_id, consumer_key, consumer_secret, key_permissions } = req.body;
-  console.log("req.body", req.body);
-  try {
-    // Store WooCommerce store data in Supabase
-    const { data: storeData, error: storeError } = await supabase
-      .from('stores')
-      .insert({
-        user_id: user_id, // This should be the authenticated user's ID
-        platform_name: platform,
-        store_name: new URL(shop_url).hostname.split('.')[0], // Extract store name from URL
-        store_url: shop_url,
-        api_key: consumer_key,
-        api_secret: consumer_secret,
-        scopes: key_permissions ? [key_permissions] : ['read_write'],
-        alt_data: {
-          key_id: key_id,
-          permissions: key_permissions
-        },
-        phone_number: phone_number,
-        package: package
-      })
-      .select()
-      .single();
-
-    if (storeError) {
-      console.error('Error storing WooCommerce store data:', storeError);
-      return res.status(500).json({
-        success: false,
-        error: 'Failed to store store data'
-      });
-    }
-
-    console.log(`Successfully stored WooCommerce store data for: ${shop_url}`);
-
-    res.json({
-      success: true,
-      message: 'Woocommerce Credentials Received Successfully...',
-    });
-
-  } catch (error) {
-    console.error('WooCommerce callback error:', error);
-    res.status(500).json({
-      success: false,
-      error: 'Failed to process WooCommerce callback'
-    });
-  }
-});
-
-// ShopRenter OAuth Routes
-
-// Route 1: Initialize ShopRenter OAuth (Entry Point)
-app.get('/auth/shoprenter', verifyShopRenterRequest, async (req, res) => {
-  try {
-    const { shopname, app_url, timestamp } = req.query;
-
-    if (!shopname || !app_url) {
-      return res.status(400).json({
-        error: 'Missing required parameters',
-        details: 'shopname and app_url are required'
-      });
-    }
-
-    console.log(`ShopRenter OAuth initiated for shop: ${shopname}`);
-
-    // Store the app_url for the callback
-    const nonce = generateNonce();
-    nonceStore.set(nonce, {
-      shopname: shopname,
-      app_url: app_url,
-      timestamp: Date.now(),
-      expiresAt: Date.now() + (10 * 60 * 1000) // 10 minutes
-    });
-
-    // Build authorization URL
-    const authParams = new URLSearchParams({
-      response_type: 'code',
-      client_id: shoprenterConfig.clientId,
-      scope: shoprenterConfig.scopes,
-      redirect_uri: shoprenterConfig.redirectUri,
-      state: nonce
-    });
-
-    const authUrl = `${app_url}/admin/oauth/authorize?${authParams.toString()}`;
-
-    console.log(`Redirecting to ShopRenter authorization: ${authUrl}`);
-    res.redirect(authUrl);
-
-  } catch (error) {
-    console.error('ShopRenter OAuth initialization error:', error);
-    res.status(500).json({
-      error: 'Failed to initialize OAuth',
-      details: error.message
-    });
-  }
-});
-
-// Route 2: Handle ShopRenter OAuth Callback
-app.get('/auth/shoprenter/callback', async (req, res) => {
-  try {
-    const { shopname, code, timestamp, hmac, app_url, state } = req.query;
-
-    // Validate HMAC
-    const validation = validateShopRenterHMAC(req.query, shoprenterConfig.clientSecret);
-    if (!validation.valid) {
-      console.error('ShopRenter callback HMAC validation failed:', validation.error);
-      return res.status(403).json({
-        error: 'Invalid request signature',
-        details: validation.error
-      });
-    }
-
-    // Validate required parameters
-    if (!shopname || !code || !app_url) {
-      return res.status(400).json({
-        error: 'Missing required parameters',
-        details: 'shopname, code, and app_url are required'
-      });
-    }
-
-    // Verify state/nonce if provided
-    if (state) {
-      const nonceData = nonceStore.get(state);
-      if (!nonceData || nonceData.shopname !== shopname) {
-        console.error('Invalid or expired state parameter');
-        return res.status(400).json({
-          error: 'Invalid state',
-          details: 'Invalid or expired state parameter'
-        });
-      }
-      // Clean up the used nonce
-      nonceStore.delete(state);
-    }
-
-    console.log(`Processing ShopRenter OAuth callback for shop: ${shopname}`);
-
-    // Exchange authorization code for access token
-    const tokenUrl = `${app_url}/admin/oauth/token`;
-    const tokenParams = new URLSearchParams({
-      grant_type: 'authorization_code',
-      client_id: shoprenterConfig.clientId,
-      client_secret: shoprenterConfig.clientSecret,
-      code: code,
-      redirect_uri: shoprenterConfig.redirectUri
-    });
-
-    const tokenResponse = await axios.post(tokenUrl, tokenParams.toString(), {
-      headers: {
-        'Content-Type': 'application/x-www-form-urlencoded'
-      }
-    });
-
-    const tokenData = tokenResponse.data;
-
-    if (!tokenData.access_token) {
-      throw new Error('No access token received from ShopRenter');
-    }
-
-    console.log(`Successfully received ShopRenter access token for: ${shopname}`);
-
-    // Calculate token expiration
-    const expiresAt = tokenData.expires_in
-      ? new Date(Date.now() + tokenData.expires_in * 1000).toISOString()
-      : null;
-
-    // Store the store information in the stores table
-    const { data: storeData, error: storeError } = await supabase
-      .from('stores')
-      .insert({
-        platform_name: 'shoprenter',
-        store_name: shopname,
-        store_url: app_url,
-        scopes: tokenData.scope ? tokenData.scope.split(' ') : shoprenterConfig.scopes.split(' '),
-        alt_data: {
-          shopname: shopname,
-          token_type: tokenData.token_type
-        }
-      })
-      .select()
-      .single();
-
-    if (storeError) {
-      console.error('Error storing ShopRenter store data:', storeError);
-      return res.status(500).json({
-        error: 'Failed to store store data',
-        details: storeError.message
-      });
-    }
-
-    // Store the ShopRenter tokens in shoprenter_tokens table
-    const { data: tokenRecord, error: tokenError } = await supabase
-      .from('shoprenter_tokens')
-      .insert({
-        store_id: storeData.id,
-        access_token: tokenData.access_token,
-        refresh_token: tokenData.refresh_token || null,
-        expires_at: expiresAt,
-        scopes: tokenData.scope ? tokenData.scope.split(' ') : shoprenterConfig.scopes.split(' '),
-        shopname: shopname,
-        shop_domain: app_url,
-        is_active: true
-      })
-      .select()
-      .single();
-
-    if (tokenError) {
-      console.error('Error storing ShopRenter token:', tokenError);
-      // Try to clean up the store record
-      await supabase.from('stores').delete().eq('id', storeData.id);
-      return res.status(500).json({
-        error: 'Failed to store token data',
-        details: tokenError.message
-      });
-    }
-
-    console.log(`Successfully stored ShopRenter connection for: ${shopname}`);
-
-    // Redirect to dashboard
-    res.redirect(`${FRONTEND_URL}/dashboard?connection=success&platform=shoprenter`);
-
-  } catch (error) {
-    console.error('ShopRenter OAuth callback error:', error);
-    res.status(500).json({
-      error: 'Failed to complete OAuth process',
-      details: error.message
-    });
-  }
-});
-
-// Route 3: Handle ShopRenter App Uninstall
-app.get('/auth/shoprenter/uninstall', verifyShopRenterRequest, async (req, res) => {
-  try {
-    const { shopname, app_url, timestamp } = req.query;
-
-    if (!shopname || !app_url) {
-      return res.status(400).json({
-        error: 'Missing required parameters',
-        details: 'shopname and app_url are required'
-      });
-    }
-
-    console.log(`ShopRenter uninstall request for shop: ${shopname}`);
-
-    // Deactivate the store tokens
-    const { data: updatedTokens, error: tokenError } = await supabase
-      .from('shoprenter_tokens')
-      .update({ is_active: false })
-      .eq('shopname', shopname)
-      .eq('shop_domain', app_url);
-
-    if (tokenError) {
-      console.error('Error deactivating ShopRenter tokens:', tokenError);
-    }
-
-    // Deactivate the store
-    const { data: updatedStore, error: storeError } = await supabase
-      .from('stores')
-      .update({ is_active: false })
-      .eq('platform_name', 'shoprenter')
-      .eq('store_url', app_url);
-
-    if (storeError) {
-      console.error('Error deactivating ShopRenter store:', storeError);
-    }
-
-    console.log(`Successfully processed uninstall for: ${shopname}`);
-
-    res.json({
-      success: true,
-      message: 'App uninstalled successfully'
-    });
-
-  } catch (error) {
-    console.error('ShopRenter uninstall error:', error);
-    res.status(500).json({
-      error: 'Failed to process uninstall',
-      details: error.message
-    });
-  }
-});
-
-// Route 2: Handle OAuth callback
-app.get('/auth/shopify/callback', async (req, res) => {
-  const { shop, code, state } = req.query;
-
-  // Validate required parameters
-  if (!shop || !code || !state) {
-    console.error('Missing required parameters in callback');
-    return res.status(400).json({
-      error: 'Missing required parameters',
-      details: 'Shop, code, and state parameters are required'
-    });
-  }
-
-  // Validate shop URL
-  const normalizedShop = normalizeShopUrl(shop);
-  if (!isValidShopUrl(normalizedShop)) {
-    console.error(`Invalid shop URL in callback: ${shop}`);
-    return res.status(400).json({
-      error: 'Invalid shop URL',
-      details: 'Invalid shop URL provided in callback'
-    });
-  }
-
-  // Verify nonce/state
-  const nonceData = nonceStore.get(state);
-  if (!nonceData || nonceData.shop !== normalizedShop) {
-    console.error('Invalid or expired state parameter');
-    return res.status(400).json({
-      error: 'Invalid state',
-      details: 'Invalid or expired state parameter'
-    });
-  }
-
-  try {
-    const tokenRes = await fetch(`https://${normalizedShop}/admin/oauth/access_token`, {
-      method: 'POST',
-      headers: { 'Content-Type': 'application/json' },
-      body: JSON.stringify({
-        client_id: config.apiKey,
-        client_secret: config.apiSecret,
-        code,
-      }),
-    });
-
-    if (!tokenRes.ok) {
-      throw new Error(`Token request failed: ${tokenRes.statusText}`);
-    }
-
-    const tokenJson = await tokenRes.json();
-
-    console.log("tokenJson", tokenJson);
-
-    // Clean up the used nonce
-    nonceStore.delete(state);
-
-    // TODO: Save tokenJson.access_token securely
-    console.log(`Successfully authenticated shop: ${normalizedShop}`);
-
-    res.redirect(`${FRONTEND_URL}/`);
-  } catch (error) {
-    console.error('OAuth callback error:', error);
-    res.status(500).json({
-      error: 'Authentication failed',
-      details: 'Failed to complete OAuth process'
-    });
-  }
-});
-
-// Route 3: Test API call with stored token
-app.get('/api/products/:shop', async (req, res) => {
-  const { shop } = req.params;
-
-  try {
-    // TODO: Retrieve access token from database
-    // const accessToken = await getStoredToken(shop);
-    const accessToken = 'your_stored_access_token';
-
-    const response = await axios.get(`https://${shop}/admin/api/2023-10/products.json`, {
-      headers: {
-        'X-Shopify-Access-Token': accessToken
-      }
-    });
-
-    res.json(response.data);
-  } catch (error) {
-    console.error('API call error:', error.response?.data || error.message);
-    res.status(500).json({ error: 'Failed to fetch products' });
-  }
-});
-
-// Webhook verification middleware
-const verifyWebhook = (req, res, next) => {
-  const shopifyHmac = req.headers['x-shopify-hmac-sha256'];
-  const rawBody = req.body;
-
-  if (!shopifyHmac || !rawBody) {
-    console.warn('❌ Missing HMAC or body in webhook request');
-    return res.status(401).send('Unauthorized');
-  }
-
-  try {
-    // Ensure rawBody is a Buffer
-    const bodyBuffer = Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody);
-    
-    const calculatedHmacDigest = crypto
-      .createHmac('sha256', config.apiSecret)
-      .update(bodyBuffer)
-      .digest('base64');
-
-    const hmacValid = crypto.timingSafeEqual(
-      Buffer.from(calculatedHmacDigest),
-      Buffer.from(shopifyHmac)
-    );
-
-    if (!hmacValid) {
-      console.warn('❌ Invalid webhook HMAC');
-      return res.status(401).send('Unauthorized');
-    }
-
-    // HMAC is valid, proceed to next middleware
-    next();
-  } catch (error) {
-    console.error('❌ Webhook verification error:', error);
-    return res.status(401).send('Unauthorized');
-  }
-};
-
-// Helper function to process webhook data
-const processWebhook = async (req, res, topic) => {
-  try {
-    const shop = req.headers['x-shopify-shop-domain'];
-    
-    // Parse the raw body as JSON
-    let data;
-    if (Buffer.isBuffer(req.body)) {
-      data = JSON.parse(req.body.toString('utf8'));
-    } else {
-      data = req.body;
-    }
-
-    console.log(`✅ Webhook received: ${topic} from ${shop}`);
-
-    // Respond quickly to Shopify (within 1 second)
-    res.status(200).send('Webhook received');
-
-    // Process webhook data asynchronously
-    // TODO: Implement your business logic here
-    // Example: Queue the webhook for processing
-    // await queueWebhook(shop, topic, data);
-
-  } catch (err) {
-    console.error(`❌ Webhook processing error for ${topic}:`, err.message);
-    // Still respond with 200 to prevent retries
-    res.status(200).send('Webhook received');
-  }
-};
-
-// GDPR Webhooks - using the correct paths that Shopify expects
-app.post('/gdpr/customers-data-request', verifyWebhook, (req, res) => {
-  processWebhook(req, res, 'customers/data_request');
-});
-
-app.post('/gdpr/customers-redact', verifyWebhook, (req, res) => {
-  processWebhook(req, res, 'customers/redact');
-});
-
-app.post('/gdpr/shop-redact', verifyWebhook, (req, res) => {
-  processWebhook(req, res, 'shop/redact');
-});
-
-// Helper function to format time ago
-const getTimeAgo = (date) => {
-  const now = new Date();
-  const past = new Date(date);
-  const diffInSeconds = Math.floor((now - past) / 1000);
-
-  if (diffInSeconds < 60) return `${diffInSeconds} seconds ago`;
-  if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)} mins ago`;
-  if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)} hours ago`;
-  return `${Math.floor(diffInSeconds / 86400)} days ago`;
-};
-
-// Helper function to format duration
-const formatDuration = (seconds) => {
-  if (!seconds) return "0:00";
-  const minutes = Math.floor(seconds / 60);
-  const remainingSeconds = seconds % 60;
-  return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
-};
-
-// Helper function to mask phone number
-const maskPhoneNumber = (phone) => {
-  if (!phone) return "...xxx-0000";
-  const last4 = phone.slice(-4);
-  return `...xxx-${last4}`;
-};
-
-// Helper function to determine sentiment
-const getSentiment = (summary) => {
-  if (!summary) return "Neutral";
-  const lowerSummary = summary.toLowerCase();
-  if (lowerSummary.includes("interested") || lowerSummary.includes("positive")) return "Positive";
-  if (lowerSummary.includes("not interested") || lowerSummary.includes("negative")) return "Negative";
-  return "Neutral";
-};
-
-// Helper function to determine intent
-const getIntent = (transcript) => {
-  if (!transcript) return "General Support";
-  const lowerTranscript = transcript.toLowerCase();
-  if (lowerTranscript.includes("order") || lowerTranscript.includes("status")) return "Order Status";
-  if (lowerTranscript.includes("return") || lowerTranscript.includes("refund")) return "Return Request";
-  if (lowerTranscript.includes("product") || lowerTranscript.includes("item")) return "Product Information";
-  if (lowerTranscript.includes("shipping") || lowerTranscript.includes("delivery")) return "Shipping Inquiry";
-  return "General Support";
-};
-
-//VAPI Routes
-
-// Get dashboard statistics
-app.get('/api/dashboard/stats', securedSession, async (req, res) => {
-  try {
-    const now = new Date();
-    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
-    const yesterday = new Date(today);
-    yesterday.setDate(yesterday.getDate() - 1);
-
-    // Get all calls for the user
-    const { data: allCalls, error: allCallsError } = await supabase
-      .from('call_logs')
-      .select('*')
-      .eq('user_id', req.user.id);
-
-    if (allCallsError) {
-      console.error('Error fetching calls for stats:', allCallsError);
-      return res.status(500).json({ error: 'Failed to fetch dashboard stats' });
-    }
-
-    // Get today's calls
-    const todayCalls = allCalls.filter(call => new Date(call.created_at) >= today);
-    const yesterdayCalls = allCalls.filter(call =>
-      new Date(call.created_at) >= yesterday && new Date(call.created_at) < today
-    );
-
-    // Calculate KPIs
-    const totalCalls = todayCalls.length;
-    const totalCallsYesterday = yesterdayCalls.length;
-    const totalCallsChange = totalCallsYesterday > 0
-      ? ((totalCalls - totalCallsYesterday) / totalCallsYesterday * 100).toFixed(1)
-      : totalCalls > 0 ? '+100.0' : '0.0';
-
-    const resolvedCalls = todayCalls.filter(c =>
-      c.call_outcome === 'resolved' || c.call_outcome === 'interested'
-    ).length;
-    const resolvedCallsYesterday = yesterdayCalls.filter(c =>
-      c.call_outcome === 'resolved' || c.call_outcome === 'interested'
-    ).length;
-    const resolvedCallsChange = resolvedCallsYesterday > 0
-      ? ((resolvedCalls - resolvedCallsYesterday) / resolvedCallsYesterday * 100).toFixed(1)
-      : resolvedCalls > 0 ? '+100.0' : '0.0';
-
-    // Calculate average call duration
-    const completedCalls = todayCalls.filter(c => c.duration_seconds && c.duration_seconds > 0);
-    const avgDuration = completedCalls.length > 0
-      ? Math.floor(completedCalls.reduce((sum, c) => sum + c.duration_seconds, 0) / completedCalls.length)
-      : 0;
-
-    const completedCallsYesterday = yesterdayCalls.filter(c => c.duration_seconds && c.duration_seconds > 0);
-    const avgDurationYesterday = completedCallsYesterday.length > 0
-      ? Math.floor(completedCallsYesterday.reduce((sum, c) => sum + c.duration_seconds, 0) / completedCallsYesterday.length)
-      : 0;
-    const avgDurationChange = avgDurationYesterday > 0
-      ? ((avgDuration - avgDurationYesterday) / avgDurationYesterday * 100).toFixed(1)
-      : '0.0';
-
-    // Calculate total cost
-    const totalCost = todayCalls.reduce((sum, c) => sum + (c.cost_total || 0), 0);
-    const totalCostYesterday = yesterdayCalls.reduce((sum, c) => sum + (c.cost_total || 0), 0);
-    const totalCostChange = totalCostYesterday > 0
-      ? ((totalCost - totalCostYesterday) / totalCostYesterday * 100).toFixed(1)
-      : totalCost > 0 ? '+100.0' : '0.0';
-
-    // Calculate time saved (assuming avg human call is 5 minutes)
-    const timeSavedSeconds = completedCalls.length * 5 * 60;
-    const timeSavedHours = (timeSavedSeconds / 3600).toFixed(1);
-    const timeSavedSecondsYesterday = completedCallsYesterday.length * 5 * 60;
-    const timeSavedHoursYesterday = (timeSavedSecondsYesterday / 3600).toFixed(1);
-    const timeSavedChange = timeSavedHoursYesterday > 0
-      ? ((timeSavedHours - timeSavedHoursYesterday) / timeSavedHoursYesterday * 100).toFixed(1)
-      : timeSavedHours > 0 ? '+100.0' : '0.0';
-
-    // Calculate saved on human costs (assuming $32/hour for human agent)
-    const humanCostSaved = (timeSavedSeconds / 3600) * 32;
-    const humanCostSavedYesterday = (timeSavedSecondsYesterday / 3600) * 32;
-    const humanCostSavedChange = humanCostSavedYesterday > 0
-      ? ((humanCostSaved - humanCostSavedYesterday) / humanCostSavedYesterday * 100).toFixed(1)
-      : humanCostSaved > 0 ? '+100.0' : '0.0';
-
-    // Calculate resolution rate
-    const resolutionRate = totalCalls > 0
-      ? ((resolvedCalls / totalCalls) * 100).toFixed(1)
-      : 0;
-    const resolutionRateYesterday = totalCallsYesterday > 0
-      ? ((resolvedCallsYesterday / totalCallsYesterday) * 100).toFixed(1)
-      : 0;
-    const dailyChange = resolutionRateYesterday > 0
-      ? (resolutionRate - resolutionRateYesterday).toFixed(1)
-      : '0.0';
-
-    // Calculate call intents (top 5)
-    const intentCounts = {};
-    todayCalls.forEach(call => {
-      const intent = getIntent(call.transcript) || 'General Support';
-      intentCounts[intent] = (intentCounts[intent] || 0) + 1;
-    });
-
-    const yesterdayIntentCounts = {};
-    yesterdayCalls.forEach(call => {
-      const intent = getIntent(call.transcript) || 'General Support';
-      yesterdayIntentCounts[intent] = (yesterdayIntentCounts[intent] || 0) + 1;
-    });
-
-    const topIntents = Object.entries(intentCounts)
-      .map(([name, count]) => {
-        const yesterdayCount = yesterdayIntentCounts[name] || 0;
-        const change = yesterdayCount > 0
-          ? ((count - yesterdayCount) / yesterdayCount * 100).toFixed(1)
-          : count > 0 ? '+100.0' : '0.0';
-        const percentage = totalCalls > 0 ? ((count / totalCalls) * 100).toFixed(1) : 0;
-        return {
-          name,
-          count,
-          percentage: parseFloat(percentage),
-          change: change > 0 ? `+${change}%` : `${change}%`
-        };
-      })
-      .sort((a, b) => b.count - a.count)
-      .slice(0, 5);
-
-    res.json({
-      success: true,
-      stats: {
-        totalCalls: {
-          value: totalCalls,
-          change: totalCallsChange > 0 ? `+${totalCallsChange}%` : `${totalCallsChange}%`,
-          changeType: totalCallsChange >= 0 ? 'positive' : 'negative'
-        },
-        resolvedCalls: {
-          value: resolvedCalls,
-          change: resolvedCallsChange > 0 ? `+${resolvedCallsChange}%` : `${resolvedCallsChange}%`,
-          changeType: resolvedCallsChange >= 0 ? 'positive' : 'negative'
-        },
-        avgDuration: {
-          value: avgDuration,
-          formatted: formatDuration(avgDuration),
-          change: avgDurationChange > 0 ? `+${avgDurationChange}%` : `${avgDurationChange}%`,
-          changeType: avgDurationChange <= 0 ? 'positive' : 'negative' // Lower duration is better
-        },
-        totalCost: {
-          value: totalCost,
-          formatted: `$${totalCost.toFixed(2)}`,
-          change: totalCostChange > 0 ? `+${totalCostChange}%` : `${totalCostChange}%`,
-          changeType: totalCostChange >= 0 ? 'positive' : 'negative'
-        },
-        timeSaved: {
-          value: timeSavedHours,
-          formatted: `${timeSavedHours}h`,
-          change: timeSavedChange > 0 ? `+${timeSavedChange}%` : `${timeSavedChange}%`,
-          changeType: timeSavedChange >= 0 ? 'positive' : 'negative'
-        },
-        humanCostSaved: {
-          value: humanCostSaved,
-          formatted: `$${humanCostSaved.toFixed(0)}`,
-          change: humanCostSavedChange > 0 ? `+${humanCostSavedChange}%` : `${humanCostSavedChange}%`,
-          changeType: humanCostSavedChange >= 0 ? 'positive' : 'negative'
-        },
-        resolutionRate: {
-          value: parseFloat(resolutionRate),
-          dailyChange: dailyChange > 0 ? `+${dailyChange}%` : `${dailyChange}%`,
-          weeklyChange: '+0.0%' // TODO: Calculate actual weekly change
-        },
-        topIntents
-      }
-    });
-
-  } catch (error) {
-    console.error('Dashboard stats error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Get call logs endpoint with securedSession middleware
-app.get('/api/call-logs', securedSession, async (req, res) => {
-  try {
-    // Get call logs for the authenticated user
-    const { data: callLogs, error: logsError } = await supabase
-      .from('call_logs')
-      .select('*')
-      .eq('user_id', req.user.id)
-      .order('created_at', { ascending: false });
-
-    if (logsError) {
-      console.error('Error fetching call logs:', logsError);
-      return res.status(500).json({ error: 'Failed to fetch call logs' });
-    }
-
-    // Transform the data into the required format
-    const transformedLogs = callLogs.map(log => {
-      const outcome = log.call_outcome || "pending";
-      const sentiment = getSentiment(log.summary);
-      const intent = getIntent(log.transcript);
-
-      // Helper to format ENUM values for display (e.g., "not_interested" -> "Not Interested")
-      const formatOutcome = (outcome) => {
-        return outcome
-          .split('_')
-          .map(word => word.charAt(0).toUpperCase() + word.slice(1))
-          .join(' ');
-      };
-
-      // Determine outcome color based on ENUM value
-      const getOutcomeColor = (outcome) => {
-        switch(outcome) {
-          case 'resolved':
-          case 'interested':
-            return 'text-green-500';
-          case 'not_interested':
-          case 'no_answer':
-          case 'busy':
-          case 'false':
-            return 'text-red-500';
-          case 'potential':
-          case 'callback_requested':
-          case 'voicemail':
-            return 'text-yellow-500';
-          default:
-            return 'text-slate-400';
-        }
-      };
-
-      return {
-        time: getTimeAgo(log.created_at),
-        customer: (log.customer_number),
-        intent: intent,
-        outcome: formatOutcome(outcome),
-        duration: formatDuration(log.duration_seconds),
-        sentiment: sentiment,
-        cost: `$${log.cost_total?.toFixed(2) || "0.00"}`,
-        outcomeColor: getOutcomeColor(outcome),
-        sentimentColor: sentiment === "Positive" ? "text-green-500" :
-          sentiment === "Negative" ? "text-red-500" :
-            "text-yellow-500"
-      };
-    });
-
-    res.json({
-      success: true,
-      call_logs: transformedLogs
-    });
-
-  } catch (error) {
-    console.error('Get call logs error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Store Management Routes
-
-// Get all stores for the authenticated user
-app.get('/api/stores', securedSession, async (req, res) => {
-  try {
-    const { data: stores, error } = await supabase
-      .from('stores')
-      .select('*')
-      .eq('user_id', req.user.id)
-      .eq('is_active', true)
-      .order('created_at', { ascending: false });
-
-    if (error) {
-      console.error('Error fetching stores:', error);
-      return res.status(500).json({ error: 'Failed to fetch stores' });
-    }
-
-    res.json({
-      success: true,
-      stores: stores
-    });
-
-  } catch (error) {
-    console.error('Get stores error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Get a specific store by ID
-app.get('/api/stores/:id', securedSession, async (req, res) => {
-  try {
-    const { id } = req.params;
-
-    const { data: store, error } = await supabase
-      .from('stores')
-      .select('*')
-      .eq('id', id)
-      .eq('user_id', req.user.id)
-      .eq('is_active', true)
-      .single();
-
-    if (error) {
-      console.error('Error fetching store:', error);
-      return res.status(404).json({ error: 'Store not found' });
-    }
-
-    res.json({
-      success: true,
-      store: store
-    });
-
-  } catch (error) {
-    console.error('Get store error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Update a store
-app.put('/api/stores/:id', securedSession, async (req, res) => {
-  try {
-    const { id } = req.params;
-    const updateData = req.body;
-
-    // Remove sensitive fields that shouldn't be updated via API
-    delete updateData.user_id;
-    delete updateData.id;
-    delete updateData.created_at;
-
-    const { data: store, error } = await supabase
-      .from('stores')
-      .update({
-        ...updateData,
-        updated_at: new Date().toISOString()
-      })
-      .eq('id', id)
-      .eq('user_id', req.user.id)
-      .select()
-      .single();
-
-    if (error) {
-      console.error('Error updating store:', error);
-      return res.status(400).json({ error: 'Failed to update store' });
-    }
-
-    res.json({
-      success: true,
-      store: store
-    });
-
-  } catch (error) {
-    console.error('Update store error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// Delete a store (soft delete by setting is_active to false)
-app.delete('/api/stores/:id', securedSession, async (req, res) => {
-  try {
-    const { id } = req.params;
-
-    const { data: store, error } = await supabase
-      .from('stores')
-      .update({
-        is_active: false,
-        updated_at: new Date().toISOString()
-      })
-      .eq('id', id)
-      .eq('user_id', req.user.id)
-      .select()
-      .single();
-
-    if (error) {
-      console.error('Error deleting store:', error);
-      return res.status(400).json({ error: 'Failed to delete store' });
-    }
-
-    res.json({
-      success: true,
-      message: 'Store deleted successfully',
-      store: store
-    });
-
-  } catch (error) {
-    console.error('Delete store error:', error);
-    res.status(500).json({ error: 'Internal server error' });
-  }
-});
-
-// ShopRenter API Integration Functions
-
-// Helper function to get active ShopRenter token for a store
-async function getShopRenterToken(storeId) {
-  const { data: tokenData, error } = await supabase
-    .from('shoprenter_tokens')
-    .select('*')
-    .eq('store_id', storeId)
-    .eq('is_active', true)
-    .single();
-
-  if (error || !tokenData) {
-    throw new Error('No active ShopRenter token found for store');
-  }
-
-  // Check if token is expired
-  if (tokenData.expires_at) {
-    const expiryDate = new Date(tokenData.expires_at);
-    if (expiryDate < new Date()) {
-      // Token expired, try to refresh
-      if (tokenData.refresh_token) {
-        return await refreshShopRenterToken(tokenData);
-      } else {
-        throw new Error('ShopRenter token expired and no refresh token available');
-      }
-    }
-  }
-
-  return tokenData;
-}
-
-// Helper function to refresh ShopRenter access token
-async function refreshShopRenterToken(tokenData) {
-  try {
-    const tokenUrl = `${tokenData.shop_domain}/admin/oauth/token`;
-    const params = new URLSearchParams({
-      grant_type: 'refresh_token',
-      client_id: shoprenterConfig.clientId,
-      client_secret: shoprenterConfig.clientSecret,
-      refresh_token: tokenData.refresh_token
-    });
-
-    const response = await axios.post(tokenUrl, params.toString(), {
-      headers: {
-        'Content-Type': 'application/x-www-form-urlencoded'
-      }
-    });
-
-    const newTokenData = response.data;
-
-    // Update token in database
-    const expiresAt = newTokenData.expires_in
-      ? new Date(Date.now() + newTokenData.expires_in * 1000).toISOString()
-      : null;
-
-    const { data: updatedToken, error } = await supabase
-      .from('shoprenter_tokens')
-      .update({
-        access_token: newTokenData.access_token,
-        refresh_token: newTokenData.refresh_token || tokenData.refresh_token,
-        expires_at: expiresAt,
-        updated_at: new Date().toISOString()
-      })
-      .eq('id', tokenData.id)
-      .select()
-      .single();
-
-    if (error) {
-      throw new Error('Failed to update refreshed token');
-    }
-
-    return updatedToken;
-  } catch (error) {
-    console.error('Token refresh error:', error);
-    throw new Error('Failed to refresh ShopRenter token');
-  }
-}
-
-// Helper function to make ShopRenter API requests
-async function makeShopRenterRequest(tokenData, endpoint, method = 'GET', data = null) {
-  try {
-    const url = `${tokenData.shop_domain}/admin/api${endpoint}`;
-    const config = {
-      method: method,
-      url: url,
-      headers: {
-        'Authorization': `Bearer ${tokenData.access_token}`,
-        'Content-Type': 'application/json'
-      }
-    };
-
-    if (data && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
-      config.data = data;
-    }
-
-    const response = await axios(config);
-    return response.data;
-  } catch (error) {
-    console.error('ShopRenter API request error:', error.response?.data || error.message);
-    throw error;
-  }
-}
-
-// Get ShopRenter products for a store
-app.get('/api/shoprenter/products/:storeId', securedSession, async (req, res) => {
-  try {
-    const { storeId } = req.params;
-
-    // Verify store belongs to user
-    const { data: store, error: storeError } = await supabase
-      .from('stores')
-      .select('*')
-      .eq('id', storeId)
-      .eq('user_id', req.user.id)
-      .eq('platform_name', 'shoprenter')
-      .single();
-
-    if (storeError || !store) {
-      return res.status(404).json({ error: 'Store not found' });
-    }
-
-    // Get ShopRenter token
-    const tokenData = await getShopRenterToken(storeId);
-
-    // Fetch products from ShopRenter API
-    const products = await makeShopRenterRequest(tokenData, '/products');
-
-    // Update cache
-    if (products && products.items) {
-      for (const product of products.items) {
-        await supabase
-          .from('shoprenter_products_cache')
-          .upsert({
-            store_id: storeId,
-            shoprenter_product_id: product.id.toString(),
-            product_data: product,
-            last_synced_at: new Date().toISOString()
-          }, {
-            onConflict: 'store_id,shoprenter_product_id'
-          });
-      }
-    }
-
-    res.json({
-      success: true,
-      products: products
-    });
-
-  } catch (error) {
-    console.error('Get ShopRenter products error:', error);
-    res.status(500).json({
-      error: 'Failed to fetch products',
-      details: error.message
-    });
-  }
-});
-
-// Get ShopRenter orders for a store
-app.get('/api/shoprenter/orders/:storeId', securedSession, async (req, res) => {
-  try {
-    const { storeId } = req.params;
-
-    // Verify store belongs to user
-    const { data: store, error: storeError } = await supabase
-      .from('stores')
-      .select('*')
-      .eq('id', storeId)
-      .eq('user_id', req.user.id)
-      .eq('platform_name', 'shoprenter')
-      .single();
-
-    if (storeError || !store) {
-      return res.status(404).json({ error: 'Store not found' });
-    }
-
-    // Get ShopRenter token
-    const tokenData = await getShopRenterToken(storeId);
-
-    // Fetch orders from ShopRenter API
-    const orders = await makeShopRenterRequest(tokenData, '/orders');
-
-    res.json({
-      success: true,
-      orders: orders
-    });
-
-  } catch (error) {
-    console.error('Get ShopRenter orders error:', error);
-    res.status(500).json({
-      error: 'Failed to fetch orders',
-      details: error.message
-    });
-  }
-});
-
-// Get ShopRenter customers for a store
-app.get('/api/shoprenter/customers/:storeId', securedSession, async (req, res) => {
-  try {
-    const { storeId } = req.params;
-
-    // Verify store belongs to user
-    const { data: store, error: storeError } = await supabase
-      .from('stores')
-      .select('*')
-      .eq('id', storeId)
-      .eq('user_id', req.user.id)
-      .eq('platform_name', 'shoprenter')
-      .single();
-
-    if (storeError || !store) {
-      return res.status(404).json({ error: 'Store not found' });
-    }
-
-    // Get ShopRenter token
-    const tokenData = await getShopRenterToken(storeId);
-
-    // Fetch customers from ShopRenter API
-    const customers = await makeShopRenterRequest(tokenData, '/customers');
-
-    res.json({
-      success: true,
-      customers: customers
-    });
-
-  } catch (error) {
-    console.error('Get ShopRenter customers error:', error);
-    res.status(500).json({
-      error: 'Failed to fetch customers',
-      details: error.message
-    });
-  }
-});
-
-// Sync ShopRenter data (products, orders, customers)
-app.post('/api/shoprenter/sync/:storeId', securedSession, async (req, res) => {
-  try {
-    const { storeId } = req.params;
-    const { syncType } = req.body; // 'products', 'orders', 'customers', or 'all'
-
-    // Verify store belongs to user
-    const { data: store, error: storeError } = await supabase
-      .from('stores')
-      .select('*')
-      .eq('id', storeId)
-      .eq('user_id', req.user.id)
-      .eq('platform_name', 'shoprenter')
-      .single();
-
-    if (storeError || !store) {
-      return res.status(404).json({ error: 'Store not found' });
-    }
-
-    // Get ShopRenter token
-    const tokenData = await getShopRenterToken(storeId);
-
-    const syncResults = {};
-
-    // Sync products
-    if (syncType === 'products' || syncType === 'all') {
-      try {
-        const products = await makeShopRenterRequest(tokenData, '/products');
-        if (products && products.items) {
-          for (const product of products.items) {
-            await supabase
-              .from('shoprenter_products_cache')
-              .upsert({
-                store_id: storeId,
-                shoprenter_product_id: product.id.toString(),
-                product_data: product,
-                last_synced_at: new Date().toISOString()
-              }, {
-                onConflict: 'store_id,shoprenter_product_id'
-              });
-          }
-          syncResults.products = { success: true, count: products.items.length };
-        }
-      } catch (error) {
-        syncResults.products = { success: false, error: error.message };
-      }
-    }
-
-    // Update last sync time
-    await supabase
-      .from('shoprenter_tokens')
-      .update({ last_sync_at: new Date().toISOString() })
-      .eq('id', tokenData.id);
-
-    res.json({
-      success: true,
-      message: 'Sync completed',
-      results: syncResults
-    });
-
-  } catch (error) {
-    console.error('ShopRenter sync error:', error);
-    res.status(500).json({
-      error: 'Failed to sync data',
-      details: error.message
-    });
-  }
-});
-
-// Cleanup expired data periodically
-setInterval(() => {
-  const now = Date.now();
-
-  // Clean up expired nonces
-  for (const [nonce, data] of nonceStore.entries()) {
-    if (now > data.expiresAt) {
-      nonceStore.delete(nonce);
-    }
-  }
-
-  // Clean up expired pending signups
-  for (const [signupId, data] of pendingSignups.entries()) {
-    if (now > data.expiresAt) {
-      pendingSignups.delete(signupId);
-    }
-  }
-}, 5 * 60 * 1000); // Clean up every 5 minutes
-
-const PORT = process.env.PORT || 3000;
-app.listen(PORT, () => {
-  console.log(`Server running on port ${PORT}`);
-  console.log(`Redirect URI: ${config.redirectUri}`);
-  console.log(`OAuth URL: http://localhost:${PORT}/auth/shopify?shop=07u1ra-rp.myshopify.com/`);
-});
-

+ 0 - 21
shopcall.ai-backend-main/package.json

@@ -1,21 +0,0 @@
-{
-  "name": "ai-caller-web-backend",
-  "version": "1.0.0",
-  "main": "index.js",
-  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1",
-    "start": "node api/index"
-  },
-  "author": "",
-  "license": "ISC",
-  "description": "",
-  "dependencies": {
-    "@supabase/supabase-js": "^2.50.0",
-    "axios": "^1.9.0",
-    "cors": "^2.8.5",
-    "crypto": "^1.0.1",
-    "dotenv": "^16.5.0",
-    "express": "^5.1.0",
-    "nodemailer": "^7.0.3"
-  }
-}

+ 0 - 159
shopcall.ai-backend-main/public/index.html

@@ -1,159 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>AI Caller - Welcome</title>
-    <style>
-        * {
-            margin: 0;
-            padding: 0;
-            box-sizing: border-box;
-        }
-
-        body {
-            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
-            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-            min-height: 100vh;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            padding: 20px;
-        }
-
-        .welcome-container {
-            background: white;
-            padding: 3rem;
-            border-radius: 20px;
-            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
-            text-align: center;
-            max-width: 500px;
-            width: 100%;
-        }
-
-        .logo {
-            font-size: 3rem;
-            font-weight: 700;
-            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-            -webkit-background-clip: text;
-            -webkit-text-fill-color: transparent;
-            background-clip: text;
-            margin-bottom: 1rem;
-        }
-
-        .welcome-title {
-            color: #333;
-            font-size: 2rem;
-            margin-bottom: 1rem;
-            font-weight: 600;
-        }
-
-        .welcome-subtitle {
-            color: #666;
-            font-size: 1.1rem;
-            margin-bottom: 2rem;
-            line-height: 1.6;
-        }
-
-        .auth-buttons {
-            display: flex;
-            gap: 1rem;
-            margin-bottom: 2rem;
-        }
-
-        .auth-btn {
-            flex: 1;
-            padding: 0.75rem 1.5rem;
-            border: none;
-            border-radius: 10px;
-            font-size: 1rem;
-            font-weight: 600;
-            text-decoration: none;
-            display: inline-block;
-            transition: all 0.3s ease;
-            cursor: pointer;
-        }
-
-        .btn-primary {
-            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-            color: white;
-        }
-
-        .btn-secondary {
-            background: white;
-            color: #667eea;
-            border: 2px solid #667eea;
-        }
-
-        .auth-btn:hover {
-            transform: translateY(-2px);
-            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
-        }
-
-        .features {
-            text-align: left;
-            margin-top: 2rem;
-        }
-
-        .features h3 {
-            color: #333;
-            margin-bottom: 1rem;
-            font-size: 1.2rem;
-        }
-
-        .features ul {
-            list-style: none;
-            color: #666;
-        }
-
-        .features li {
-            padding: 0.5rem 0;
-            position: relative;
-            padding-left: 1.5rem;
-        }
-
-        .features li::before {
-            content: '✓';
-            position: absolute;
-            left: 0;
-            color: #667eea;
-            font-weight: bold;
-        }
-
-        @media (max-width: 480px) {
-            .auth-buttons {
-                flex-direction: column;
-            }
-            
-            .welcome-container {
-                padding: 2rem;
-            }
-        }
-    </style>
-</head>
-<body>
-    <div class="welcome-container">
-        <div class="logo">AI Caller</div>
-        <h1 class="welcome-title">Welcome to AI Caller</h1>
-        <p class="welcome-subtitle">
-            Revolutionize your business communication with intelligent AI-powered calling solutions.
-        </p>
-
-        <div class="auth-buttons">
-            <a href="/login" class="auth-btn btn-primary">Sign In</a>
-            <a href="/signup" class="auth-btn btn-secondary">Create Account</a>
-        </div>
-
-        <div class="features">
-            <h3>Key Features</h3>
-            <ul>
-                <li>AI-powered automated calling</li>
-                <li>Shopify integration</li>
-                <li>Real-time analytics</li>
-                <li>Custom voice profiles</li>
-                <li>Advanced scheduling</li>
-            </ul>
-        </div>
-    </div>
-</body>
-</html> 

+ 0 - 602
shopcall.ai-backend-main/public/login.html

@@ -1,602 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Login - AI Caller</title>
-    <style>
-        * {
-            margin: 0;
-            padding: 0;
-            box-sizing: border-box;
-        }
-
-        body {
-            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
-            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-            min-height: 100vh;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            padding: 20px;
-        }
-
-        .login-container {
-            background: white;
-            padding: 2rem;
-            border-radius: 20px;
-            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
-            width: 100%;
-            max-width: 400px;
-            position: relative;
-            overflow: hidden;
-        }
-
-        .login-container::before {
-            content: '';
-            position: absolute;
-            top: 0;
-            left: 0;
-            right: 0;
-            height: 4px;
-            background: linear-gradient(90deg, #667eea, #764ba2);
-        }
-
-        .login-header {
-            text-align: center;
-            margin-bottom: 2rem;
-        }
-
-        .login-title {
-            color: #333;
-            font-size: 2rem;
-            margin-bottom: 0.5rem;
-            font-weight: 600;
-        }
-
-        .login-subtitle {
-            color: #666;
-            font-size: 0.9rem;
-        }
-
-        .form-group {
-            margin-bottom: 1.5rem;
-        }
-
-        .form-label {
-            display: block;
-            color: #333;
-            font-weight: 500;
-            margin-bottom: 0.5rem;
-            font-size: 0.9rem;
-        }
-
-        .form-input {
-            width: 100%;
-            padding: 0.75rem 1rem;
-            border: 2px solid #e1e5e9;
-            border-radius: 10px;
-            font-size: 1rem;
-            transition: all 0.3s ease;
-            background: #f8f9fa;
-        }
-
-        .form-input:focus {
-            outline: none;
-            border-color: #667eea;
-            background: white;
-            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
-        }
-
-        .login-btn {
-            width: 100%;
-            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-            color: white;
-            border: none;
-            padding: 0.75rem 1rem;
-            font-size: 1rem;
-            font-weight: 600;
-            border-radius: 10px;
-            cursor: pointer;
-            transition: all 0.3s ease;
-            margin-bottom: 1.5rem;
-        }
-
-        .login-btn:hover {
-            transform: translateY(-2px);
-            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
-        }
-
-        .login-btn:active {
-            transform: translateY(0);
-        }
-
-        .login-btn:disabled {
-            opacity: 0.6;
-            cursor: not-allowed;
-            transform: none;
-        }
-
-        .signup-link {
-            text-align: center;
-            color: #666;
-            font-size: 0.9rem;
-        }
-
-        .signup-link a {
-            color: #667eea;
-            text-decoration: none;
-            font-weight: 600;
-        }
-
-        .signup-link a:hover {
-            text-decoration: underline;
-        }
-
-        .error-message {
-            background: #fee;
-            color: #c33;
-            padding: 0.75rem;
-            border-radius: 8px;
-            margin-bottom: 1rem;
-            font-size: 0.9rem;
-            display: none;
-        }
-
-        .success-message {
-            background: #efe;
-            color: #363;
-            padding: 0.75rem;
-            border-radius: 8px;
-            margin-bottom: 1rem;
-            font-size: 0.9rem;
-            display: none;
-        }
-
-        .loading {
-            display: inline-block;
-            width: 20px;
-            height: 20px;
-            border: 3px solid #ffffff;
-            border-radius: 50%;
-            border-top-color: transparent;
-            animation: spin 1s ease-in-out infinite;
-        }
-
-        @keyframes spin {
-            to { transform: rotate(360deg); }
-        }
-
-        /* OTP Modal Styles */
-        .otp-modal {
-            position: fixed;
-            top: 0;
-            left: 0;
-            width: 100%;
-            height: 100%;
-            background: rgba(0, 0, 0, 0.5);
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            z-index: 1000;
-            padding: 20px;
-        }
-
-        .otp-modal-content {
-            background: white;
-            padding: 2rem;
-            border-radius: 20px;
-            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
-            width: 100%;
-            max-width: 400px;
-            text-align: center;
-        }
-
-        .otp-header h2 {
-            color: #333;
-            margin-bottom: 0.5rem;
-            font-size: 1.5rem;
-        }
-
-        .otp-header p {
-            color: #666;
-            margin-bottom: 2rem;
-            font-size: 0.9rem;
-        }
-
-        .otp-input-group {
-            display: flex;
-            gap: 0.5rem;
-            justify-content: center;
-            margin-bottom: 2rem;
-        }
-
-        .otp-input {
-            width: 50px;
-            height: 50px;
-            border: 2px solid #e1e5e9;
-            border-radius: 10px;
-            text-align: center;
-            font-size: 1.25rem;
-            font-weight: 600;
-            background: #f8f9fa;
-            transition: all 0.3s ease;
-        }
-
-        .otp-input:focus {
-            outline: none;
-            border-color: #667eea;
-            background: white;
-            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
-        }
-
-        .otp-input.filled {
-            border-color: #667eea;
-            background: white;
-        }
-
-        .resend-section {
-            margin-top: 1rem;
-            font-size: 0.9rem;
-            color: #666;
-        }
-
-        .resend-btn {
-            background: none;
-            border: none;
-            color: #667eea;
-            cursor: pointer;
-            font-weight: 600;
-            text-decoration: underline;
-        }
-
-        .resend-btn:hover {
-            color: #764ba2;
-        }
-
-        .resend-btn:disabled {
-            color: #999;
-            cursor: not-allowed;
-            text-decoration: none;
-        }
-    </style>
-</head>
-<body>
-    <div class="login-container">
-        <div class="login-header">
-            <h1 class="login-title">Welcome Back</h1>
-            <p class="login-subtitle">Sign in to your account</p>
-        </div>
-
-        <div id="errorMessage" class="error-message"></div>
-        <div id="successMessage" class="success-message"></div>
-
-        <form id="loginForm">
-            <div class="form-group">
-                <label for="email" class="form-label">Email Address</label>
-                <input 
-                    type="email" 
-                    id="email" 
-                    name="email" 
-                    class="form-input" 
-                    required 
-                    placeholder="Enter your email"
-                >
-            </div>
-
-            <div class="form-group">
-                <label for="password" class="form-label">Password</label>
-                <input 
-                    type="password" 
-                    id="password" 
-                    name="password" 
-                    class="form-input" 
-                    required 
-                    placeholder="Enter your password"
-                >
-            </div>
-
-            <button type="submit" class="login-btn" id="loginBtn">
-                <span id="btnText">Sign In</span>
-                <span id="btnLoader" class="loading" style="display: none;"></span>
-            </button>
-        </form>
-
-        <div class="signup-link">
-            Don't have an account? <a href="/signup">Sign up here</a>
-        </div>
-    </div>
-
-    <!-- OTP Verification Modal -->
-    <div id="otpModal" class="otp-modal" style="display: none;">
-        <div class="otp-modal-content">
-            <div class="otp-header">
-                <h2>Enter Verification Code</h2>
-                <p>We've sent a 6-digit code to your email</p>
-            </div>
-            
-            <div id="otpErrorMessage" class="error-message"></div>
-            
-            <form id="otpForm">
-                <div class="otp-input-group">
-                    <input type="text" class="otp-input" maxlength="1" data-index="0">
-                    <input type="text" class="otp-input" maxlength="1" data-index="1">
-                    <input type="text" class="otp-input" maxlength="1" data-index="2">
-                    <input type="text" class="otp-input" maxlength="1" data-index="3">
-                    <input type="text" class="otp-input" maxlength="1" data-index="4">
-                    <input type="text" class="otp-input" maxlength="1" data-index="5">
-                </div>
-                
-                <button type="submit" class="login-btn" id="otpVerifyBtn">
-                    <span id="otpBtnText">Verify Code</span>
-                    <span id="otpBtnLoader" class="loading" style="display: none;"></span>
-                </button>
-                
-                <div class="resend-section">
-                    <p>Didn't receive the code? 
-                        <button type="button" id="resendOtpBtn" class="resend-btn">Resend Code</button>
-                    </p>
-                </div>
-            </form>
-        </div>
-    </div>
-
-    <script>
-        const loginForm = document.getElementById('loginForm');
-        const loginBtn = document.getElementById('loginBtn');
-        const btnText = document.getElementById('btnText');
-        const btnLoader = document.getElementById('btnLoader');
-        const errorMessage = document.getElementById('errorMessage');
-        const successMessage = document.getElementById('successMessage');
-        
-        // OTP Modal elements
-        const otpModal = document.getElementById('otpModal');
-        const otpForm = document.getElementById('otpForm');
-        const otpInputs = document.querySelectorAll('.otp-input');
-        const otpVerifyBtn = document.getElementById('otpVerifyBtn');
-        const otpBtnText = document.getElementById('otpBtnText');
-        const otpBtnLoader = document.getElementById('otpBtnLoader');
-        const otpErrorMessage = document.getElementById('otpErrorMessage');
-        const resendOtpBtn = document.getElementById('resendOtpBtn');
-        
-        let currentLoginId = null;
-        let currentEmail = null;
-
-        function showError(message) {
-            errorMessage.textContent = message;
-            errorMessage.style.display = 'block';
-            successMessage.style.display = 'none';
-        }
-
-        function showSuccess(message) {
-            successMessage.textContent = message;
-            successMessage.style.display = 'block';
-            errorMessage.style.display = 'none';
-        }
-
-        function hideMessages() {
-            errorMessage.style.display = 'none';
-            successMessage.style.display = 'none';
-        }
-
-        function showOtpError(message) {
-            otpErrorMessage.textContent = message;
-            otpErrorMessage.style.display = 'block';
-        }
-
-        function hideOtpError() {
-            otpErrorMessage.style.display = 'none';
-        }
-
-        function setLoading(loading) {
-            loginBtn.disabled = loading;
-            if (loading) {
-                btnText.style.display = 'none';
-                btnLoader.style.display = 'inline-block';
-            } else {
-                btnText.style.display = 'inline';
-                btnLoader.style.display = 'none';
-            }
-        }
-
-        function setOtpLoading(loading) {
-            otpVerifyBtn.disabled = loading;
-            if (loading) {
-                otpBtnText.style.display = 'none';
-                otpBtnLoader.style.display = 'inline-block';
-            } else {
-                otpBtnText.style.display = 'inline';
-                otpBtnLoader.style.display = 'none';
-            }
-        }
-
-        function showOtpModal() {
-            otpModal.style.display = 'flex';
-            otpInputs[0].focus();
-        }
-
-        function hideOtpModal() {
-            otpModal.style.display = 'none';
-            // Clear OTP inputs
-            otpInputs.forEach(input => {
-                input.value = '';
-                input.classList.remove('filled');
-            });
-            hideOtpError();
-        }
-
-        // Handle OTP input behavior
-        otpInputs.forEach((input, index) => {
-            input.addEventListener('input', (e) => {
-                const value = e.target.value;
-                
-                if (value && /^\d$/.test(value)) {
-                    input.classList.add('filled');
-                    // Move to next input
-                    if (index < otpInputs.length - 1) {
-                        otpInputs[index + 1].focus();
-                    }
-                } else {
-                    input.classList.remove('filled');
-                }
-            });
-
-            input.addEventListener('keydown', (e) => {
-                if (e.key === 'Backspace' && !input.value && index > 0) {
-                    otpInputs[index - 1].focus();
-                }
-            });
-
-            input.addEventListener('paste', (e) => {
-                e.preventDefault();
-                const pastedData = e.clipboardData.getData('text');
-                if (/^\d{6}$/.test(pastedData)) {
-                    [...pastedData].forEach((char, i) => {
-                        if (i < otpInputs.length) {
-                            otpInputs[i].value = char;
-                            otpInputs[i].classList.add('filled');
-                        }
-                    });
-                }
-            });
-        });
-
-        // Step 1: Login with email/password
-        loginForm.addEventListener('submit', async (e) => {
-            e.preventDefault();
-            hideMessages();
-            setLoading(true);
-
-            const formData = new FormData(loginForm);
-            const data = {
-                email: formData.get('email'),
-                password: formData.get('password')
-            };
-
-            currentEmail = data.email;
-
-            try {
-                const response = await fetch('/auth/login', {
-                    method: 'POST',
-                    headers: {
-                        'Content-Type': 'application/json',
-                    },
-                    body: JSON.stringify(data)
-                });
-
-                const result = await response.json();
-
-                if (response.ok) {
-                    if (result.requiresOtp) {
-                        currentLoginId = result.loginId;
-                        showSuccess('Password verified! Check your email for the verification code.');
-                        showOtpModal();
-                    } else {
-                        // Direct login without OTP
-                        showSuccess('Login successful! Redirecting...');
-                        if (result.session && result.session.access_token) {
-                            localStorage.setItem('access_token', result.session.access_token);
-                        }
-                        setTimeout(() => {
-                            window.location.href = '/';
-                        }, 1500);
-                    }
-                } else {
-                    showError(result.error || 'Login failed');
-                }
-            } catch (error) {
-                console.error('Login error:', error);
-                showError('Network error. Please try again.');
-            } finally {
-                setLoading(false);
-            }
-        });
-
-        // Step 2: Verify OTP
-        otpForm.addEventListener('submit', async (e) => {
-            e.preventDefault();
-            hideOtpError();
-            setOtpLoading(true);
-
-            // Get OTP from inputs
-            const otp = Array.from(otpInputs).map(input => input.value).join('');
-            
-            if (otp.length !== 6) {
-                showOtpError('Please enter the complete 6-digit code');
-                setOtpLoading(false);
-                return;
-            }
-
-            try {
-                const response = await fetch('/auth/login/verify', {
-                    method: 'POST',
-                    headers: {
-                        'Content-Type': 'application/json',
-                    },
-                    body: JSON.stringify({
-                        loginId: currentLoginId,
-                        otp: otp
-                    })
-                });
-
-                const result = await response.json();
-
-                if (response.ok) {
-                    hideOtpModal();
-                    showSuccess('Login successful! Redirecting...');
-                    
-                    // Store the session token
-                    if (result.session && result.session.access_token) {
-                        localStorage.setItem('access_token', result.session.access_token);
-                    }
-                    
-                    // Redirect to dashboard or home page
-                    setTimeout(() => {
-                        window.location.href = '/';
-                    }, 1500);
-                } else {
-                    showOtpError(result.error || 'Invalid verification code');
-                }
-            } catch (error) {
-                console.error('OTP verification error:', error);
-                showOtpError('Network error. Please try again.');
-            } finally {
-                setOtpLoading(false);
-            }
-        });
-
-        // Resend OTP
-        resendOtpBtn.addEventListener('click', async () => {
-            resendOtpBtn.disabled = true;
-            resendOtpBtn.textContent = 'Sending...';
-
-            try {
-                const response = await fetch('/auth/resend-otp', {
-                    method: 'POST',
-                    headers: {
-                        'Content-Type': 'application/json',
-                    },
-                    body: JSON.stringify({
-                        email: currentEmail,
-                        type: 'login'
-                    })
-                });
-
-                const result = await response.json();
-
-                if (response.ok) {
-                    showOtpError('Code resent to your email');
-                    setTimeout(() => hideOtpError(), 3000);
-                } else {
-                    showOtpError('Failed to resend code');
-                }
-            } catch (error) {
-                showOtpError('Network error. Please try again.');
-            } finally {
-                resendOtpBtn.disabled = false;
-                resendOtpBtn.textContent = 'Resend Code';
-            }
-        });
-    </script>
-</body>
-</html> 

+ 0 - 482
shopcall.ai-backend-main/public/signup.html

@@ -1,482 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Sign Up - AI Caller</title>
-    <style>
-        * {
-            margin: 0;
-            padding: 0;
-            box-sizing: border-box;
-        }
-
-        body {
-            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
-            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-            min-height: 100vh;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            padding: 20px;
-        }
-
-        .signup-container {
-            background: white;
-            padding: 2rem;
-            border-radius: 20px;
-            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
-            width: 100%;
-            max-width: 450px;
-            position: relative;
-            overflow: hidden;
-        }
-
-        .signup-container::before {
-            content: '';
-            position: absolute;
-            top: 0;
-            left: 0;
-            right: 0;
-            height: 4px;
-            background: linear-gradient(90deg, #667eea, #764ba2);
-        }
-
-        .signup-header {
-            text-align: center;
-            margin-bottom: 2rem;
-        }
-
-        .signup-title {
-            color: #333;
-            font-size: 2rem;
-            margin-bottom: 0.5rem;
-            font-weight: 600;
-        }
-
-        .signup-subtitle {
-            color: #666;
-            font-size: 0.9rem;
-        }
-
-        .form-group {
-            margin-bottom: 1.5rem;
-        }
-
-        .form-label {
-            display: block;
-            color: #333;
-            font-weight: 500;
-            margin-bottom: 0.5rem;
-            font-size: 0.9rem;
-        }
-
-        .form-input {
-            width: 100%;
-            padding: 0.75rem 1rem;
-            border: 2px solid #e1e5e9;
-            border-radius: 10px;
-            font-size: 1rem;
-            transition: all 0.3s ease;
-            background: #f8f9fa;
-        }
-
-        .form-input:focus {
-            outline: none;
-            border-color: #667eea;
-            background: white;
-            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
-        }
-
-        .form-row {
-            display: flex;
-            gap: 1rem;
-        }
-
-        .form-row .form-group {
-            flex: 1;
-        }
-
-        .signup-btn {
-            width: 100%;
-            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-            color: white;
-            border: none;
-            padding: 0.75rem 1rem;
-            font-size: 1rem;
-            font-weight: 600;
-            border-radius: 10px;
-            cursor: pointer;
-            transition: all 0.3s ease;
-            margin-bottom: 1.5rem;
-        }
-
-        .signup-btn:hover {
-            transform: translateY(-2px);
-            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
-        }
-
-        .signup-btn:active {
-            transform: translateY(0);
-        }
-
-        .signup-btn:disabled {
-            opacity: 0.6;
-            cursor: not-allowed;
-            transform: none;
-        }
-
-        .login-link {
-            text-align: center;
-            color: #666;
-            font-size: 0.9rem;
-        }
-
-        .login-link a {
-            color: #667eea;
-            text-decoration: none;
-            font-weight: 600;
-        }
-
-        .login-link a:hover {
-            text-decoration: underline;
-        }
-
-        .error-message {
-            background: #fee;
-            color: #c33;
-            padding: 0.75rem;
-            border-radius: 8px;
-            margin-bottom: 1rem;
-            font-size: 0.9rem;
-            display: none;
-        }
-
-        .success-message {
-            background: #efe;
-            color: #363;
-            padding: 0.75rem;
-            border-radius: 8px;
-            margin-bottom: 1rem;
-            font-size: 0.9rem;
-            display: none;
-        }
-
-        .loading {
-            display: inline-block;
-            width: 20px;
-            height: 20px;
-            border: 3px solid #ffffff;
-            border-radius: 50%;
-            border-top-color: transparent;
-            animation: spin 1s ease-in-out infinite;
-        }
-
-        @keyframes spin {
-            to { transform: rotate(360deg); }
-        }
-
-        .password-strength {
-            margin-top: 0.5rem;
-            font-size: 0.8rem;
-        }
-
-        .strength-indicator {
-            display: flex;
-            gap: 4px;
-            margin: 0.25rem 0;
-        }
-
-        .strength-bar {
-            height: 3px;
-            flex: 1;
-            background: #e1e5e9;
-            border-radius: 2px;
-            transition: background 0.3s ease;
-        }
-
-        .strength-bar.active {
-            background: #667eea;
-        }
-
-        @media (max-width: 480px) {
-            .form-row {
-                flex-direction: column;
-                gap: 0;
-            }
-        }
-    </style>
-</head>
-<body>
-    <div class="signup-container">
-        <div class="signup-header">
-            <h1 class="signup-title">Create Account</h1>
-            <p class="signup-subtitle">Join us and get started today</p>
-        </div>
-
-        <div id="errorMessage" class="error-message"></div>
-        <div id="successMessage" class="success-message"></div>
-
-        <form id="signupForm">
-            <div class="form-group">
-                <label for="full_name" class="form-label">Full Name</label>
-                <input 
-                    type="text" 
-                    id="full_name" 
-                    name="full_name" 
-                    class="form-input" 
-                    required 
-                    placeholder="Enter your full name"
-                >
-            </div>
-
-            <div class="form-row">
-                <div class="form-group">
-                    <label for="user_name" class="form-label">Username</label>
-                    <input 
-                        type="text" 
-                        id="user_name" 
-                        name="user_name" 
-                        class="form-input" 
-                        required 
-                        placeholder="Choose a username"
-                    >
-                </div>
-
-                <div class="form-group">
-                    <label for="company_name" class="form-label">Company Name</label>
-                    <input 
-                        type="text" 
-                        id="company_name" 
-                        name="company_name" 
-                        class="form-input" 
-                        required 
-                        placeholder="Your company"
-                    >
-                </div>
-            </div>
-
-            <div class="form-group">
-                <label for="email" class="form-label">Email Address</label>
-                <input 
-                    type="email" 
-                    id="email" 
-                    name="email" 
-                    class="form-input" 
-                    required 
-                    placeholder="Enter your email"
-                >
-            </div>
-
-            <div class="form-group">
-                <label for="password" class="form-label">Password</label>
-                <input 
-                    type="password" 
-                    id="password" 
-                    name="password" 
-                    class="form-input" 
-                    required 
-                    placeholder="Create a strong password"
-                    minlength="6"
-                >
-                <div class="password-strength">
-                    <div class="strength-indicator">
-                        <div class="strength-bar" id="bar1"></div>
-                        <div class="strength-bar" id="bar2"></div>
-                        <div class="strength-bar" id="bar3"></div>
-                        <div class="strength-bar" id="bar4"></div>
-                    </div>
-                    <div id="strengthText" style="color: #666;"></div>
-                </div>
-            </div>
-
-            <div class="form-group">
-                <label for="confirm_password" class="form-label">Confirm Password</label>
-                <input 
-                    type="password" 
-                    id="confirm_password" 
-                    name="confirm_password" 
-                    class="form-input" 
-                    required 
-                    placeholder="Confirm your password"
-                >
-            </div>
-
-            <button type="submit" class="signup-btn" id="signupBtn">
-                <span id="btnText">Create Account</span>
-                <span id="btnLoader" class="loading" style="display: none;"></span>
-            </button>
-        </form>
-
-        <div class="login-link">
-            Already have an account? <a href="/login">Sign in here</a>
-        </div>
-    </div>
-
-    <script>
-        const signupForm = document.getElementById('signupForm');
-        const signupBtn = document.getElementById('signupBtn');
-        const btnText = document.getElementById('btnText');
-        const btnLoader = document.getElementById('btnLoader');
-        const errorMessage = document.getElementById('errorMessage');
-        const successMessage = document.getElementById('successMessage');
-        const passwordInput = document.getElementById('password');
-        const confirmPasswordInput = document.getElementById('confirm_password');
-        const strengthText = document.getElementById('strengthText');
-
-        function showError(message) {
-            errorMessage.textContent = message;
-            errorMessage.style.display = 'block';
-            successMessage.style.display = 'none';
-        }
-
-        function showSuccess(message) {
-            successMessage.textContent = message;
-            successMessage.style.display = 'block';
-            errorMessage.style.display = 'none';
-        }
-
-        function hideMessages() {
-            errorMessage.style.display = 'none';
-            successMessage.style.display = 'none';
-        }
-
-        function setLoading(loading) {
-            signupBtn.disabled = loading;
-            if (loading) {
-                btnText.style.display = 'none';
-                btnLoader.style.display = 'inline-block';
-            } else {
-                btnText.style.display = 'inline';
-                btnLoader.style.display = 'none';
-            }
-        }
-
-        function checkPasswordStrength(password) {
-            let strength = 0;
-            let feedback = '';
-
-            if (password.length >= 8) strength++;
-            if (/[a-z]/.test(password)) strength++;
-            if (/[A-Z]/.test(password)) strength++;
-            if (/[0-9]/.test(password)) strength++;
-            if (/[^A-Za-z0-9]/.test(password)) strength++;
-
-            // Update strength bars
-            for (let i = 1; i <= 4; i++) {
-                const bar = document.getElementById(`bar${i}`);
-                if (i <= strength) {
-                    bar.classList.add('active');
-                } else {
-                    bar.classList.remove('active');
-                }
-            }
-
-            // Update feedback text
-            switch (strength) {
-                case 0:
-                case 1:
-                    feedback = 'Very weak password';
-                    break;
-                case 2:
-                    feedback = 'Weak password';
-                    break;
-                case 3:
-                    feedback = 'Good password';
-                    break;
-                case 4:
-                case 5:
-                    feedback = 'Strong password';
-                    break;
-            }
-
-            strengthText.textContent = feedback;
-            return strength;
-        }
-
-        passwordInput.addEventListener('input', (e) => {
-            checkPasswordStrength(e.target.value);
-        });
-
-        signupForm.addEventListener('submit', async (e) => {
-            e.preventDefault();
-            hideMessages();
-
-            const formData = new FormData(signupForm);
-            const password = formData.get('password');
-            const confirmPassword = formData.get('confirm_password');
-
-            // Validate passwords match
-            if (password !== confirmPassword) {
-                showError('Passwords do not match');
-                return;
-            }
-
-            // Validate password strength
-            if (checkPasswordStrength(password) < 2) {
-                showError('Password is too weak. Please use a stronger password.');
-                return;
-            }
-
-            setLoading(true);
-
-            const data = {
-                full_name: formData.get('full_name'),
-                company_name: formData.get('company_name'),
-                user_name: formData.get('user_name'),
-                email: formData.get('email'),
-                password: password
-            };
-
-            try {
-                const response = await fetch('/auth/signup', {
-                    method: 'POST',
-                    headers: {
-                        'Content-Type': 'application/json',
-                    },
-                    body: JSON.stringify(data)
-                });
-
-                const result = await response.json();
-
-                if (response.ok) {
-                    if (result.requiresEmailConfirmation) {
-                        showSuccess('Account created successfully! Please check your email to confirm your account, then you can sign in.');
-                    } else {
-                        showSuccess('Account created successfully! You can now sign in.');
-                        // Redirect to login page after a brief delay
-                        setTimeout(() => {
-                            window.location.href = '/login';
-                        }, 2000);
-                    }
-                    signupForm.reset();
-                    // Clear password strength indicator
-                    for (let i = 1; i <= 4; i++) {
-                        document.getElementById(`bar${i}`).classList.remove('active');
-                    }
-                    strengthText.textContent = '';
-                } else {
-                    showError(result.error || 'Signup failed');
-                }
-            } catch (error) {
-                console.error('Signup error:', error);
-                showError('Network error. Please try again.');
-            } finally {
-                setLoading(false);
-            }
-        });
-
-        // Real-time username validation
-        document.getElementById('user_name').addEventListener('input', (e) => {
-            const username = e.target.value;
-            const regex = /^[a-zA-Z0-9_]+$/;
-            
-            if (username && !regex.test(username)) {
-                e.target.style.borderColor = '#c33';
-            } else {
-                e.target.style.borderColor = '#e1e5e9';
-            }
-        });
-    </script>
-</body>
-</html> 

+ 0 - 4
shopcall.ai-backend-main/vercel.json

@@ -1,4 +0,0 @@
-{
-	"version": 2,
-	"rewrites": [{ "source": "/(.*)", "destination": "/api" }]
-}

+ 0 - 5
shopcall.ai-main/vercel.json

@@ -1,5 +0,0 @@
-{
-    "rewrites": [
-      { "source": "/(.*)", "destination": "/" }
-    ]
-}