# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview 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 - **supabase**: Supabase Edge Functions for backend API ## Repository Structure ``` /data/shopcall/ ├── shopcall.ai-main/ # Frontend application │ ├── src/ │ │ ├── components/ # React components │ │ │ ├── context/ # React context providers (AuthContext) │ │ │ └── ui/ # shadcn-ui component library │ │ ├── pages/ # Route pages (Dashboard, Login, CallLogs, etc.) │ │ ├── hooks/ # Custom React hooks │ │ ├── lib/ # Utility functions │ │ └── App.tsx # Main application with routing │ └── package.json └── supabase/ # Supabase Edge Functions └── functions/ # Backend API functions ``` ## Development Commands ### Frontend (shopcall.ai-main) ```bash cd shopcall.ai-main npm install # Install dependencies npm run dev # Start dev server on port 8080 npm run build # Production build npm run build:dev # Development build npm run lint # Run ESLint npm run preview # Preview production build ``` ### Supabase Edge Functions **Supabase CLI Location**: `~/bin/supabase` (run from `/home/claude/shopcall/supabase` directory) ```bash cd /home/claude/shopcall/supabase # Deploy Edge Functions ~/bin/supabase functions deploy # Deploy with project ref ~/bin/supabase functions deploy --project-ref ztklqodcdjeqpsvhlpud # Test locally ~/bin/supabase functions serve # Login (if needed) ~/bin/supabase login # Link to project (if needed) ~/bin/supabase link --project-ref ztklqodcdjeqpsvhlpud ``` ## Technology Stack ### Frontend - **Framework**: React 18 with Vite - **Language**: TypeScript - **UI Library**: shadcn-ui with Radix UI primitives - **Styling**: Tailwind CSS - **Routing**: React Router v6 - **State Management**: React Query (@tanstack/react-query) - **Form Handling**: React Hook Form with Zod validation - **Theme**: next-themes for dark mode support ### Backend - **Platform**: Supabase Edge Functions (Deno) - **Database**: Supabase (PostgreSQL with auth) - **Authentication**: Supabase Auth ## Architecture ### Frontend Architecture **Authentication Flow**: - 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 via Supabase client **Routing Structure**: ``` / (Index) → Landing page /signup → User registration /login → User login /otp → OTP verification /dashboard (protected) → Main dashboard /call-logs (protected) → Call history /analytics (protected) → Analytics dashboard /webshops (protected) → E-commerce integrations /phone-numbers (protected) → Phone number management /ai-config (protected) → AI configuration /onboarding (protected) → User onboarding flow /about, /privacy, /terms → Public pages ``` **Component Organization**: - Page components in `src/pages/` handle routing - Content components (e.g., `DashboardContent.tsx`) contain page logic - UI components in `src/components/ui/` are reusable shadcn components - Context providers wrap the app in `App.tsx` ### Backend Architecture **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 integration** (fully implemented) - OAuth flow: `oauth-shopify` (OAuth 2.0 with HMAC verification) - GDPR webhooks: `webhooks-shopify` (customers/data_request, customers/redact, shop/redact) - API client: `_shared/shopify-client.ts` - **Data synchronization**: `shopify-sync` (manual sync for products, orders, customers) - **Scheduled background sync**: `shopify-scheduled-sync` (automated via pg_cron) - NOT YET IMPLEMENTED - Read-only access with scopes: read_products, read_orders, read_customers, read_inventory, read_price_rules - Secure OAuth 2.0 with HMAC-SHA256 signature validation - Rate limiting (5 req/sec) and retry logic with exponential backoff - Automatic pagination support for all data types - **Shopify App Store compliant** - includes mandatory GDPR webhooks - API Version: Shopify Admin API 2024-01 - **WooCommerce integration** (fully implemented) - **Authentication**: Manual API key connection (Consumer Key + Consumer Secret) - **NO OAuth flow** - Uses direct API key authentication with OAuth 1.0a signatures - Edge Function: `oauth-woocommerce` with action `connect_manual` - API client: `_shared/woocommerce-client.ts` - **Data synchronization**: `woocommerce-sync` (manual sync for products, orders, customers) - **Scheduled background sync**: `woocommerce-scheduled-sync` (automated via pg_cron, hourly) - **API Version**: WooCommerce REST API v3 (`/wp-json/wc/v3/`) - **IMPORTANT**: Uses latest WooCommerce REST API v3 (NOT legacy API) - Official documentation: https://woocommerce.github.io/woocommerce-rest-api-docs - Requires: WooCommerce 3.5+ and WordPress 4.4+ - Legacy API versions (v1, v2, v3 at `/wc-api/v*`) are deprecated and NOT supported - Read-only access to products, orders, and customers - Secure OAuth 1.0a with HMAC-SHA256 signatures for API requests - Rate limiting (5 req/sec) and retry logic with exponential backoff - Pagination support for large datasets - Connection test validates credentials before storing - **ShopRenter integration** (fully implemented) - OAuth flow: `oauth-shoprenter-init`, `oauth-shoprenter-callback` - Uninstall webhook: `webhook-shoprenter-uninstall` - Data sync endpoints: `shoprenter-products`, `shoprenter-orders`, `shoprenter-customers` - Manual sync: `shoprenter-sync` - **Scheduled background sync**: `shoprenter-scheduled-sync` (automated via pg_cron, hourly) - API client: `_shared/shoprenter-client.ts` - Token refresh: Automatic access token refresh when expired - Store credentials in Supabase `stores` table ### Database Schema (Supabase) **stores table**: ``` - id: UUID (primary key) - user_id: UUID (FK to profiles.id) - platform_name: text (e.g., 'shopify', 'woocommerce', 'shoprenter') - store_name: text - store_url: text - api_key: text (Consumer Key for WooCommerce, Access Token for ShopRenter/Shopify) - api_secret: text (Consumer Secret for WooCommerce, Refresh Token for ShopRenter) - access_token: text (for platforms using separate access_token field) - refresh_token: text (for platforms using separate refresh_token field) - token_expires_at: timestamp (token expiration time) - scopes: text[] - alt_data: jsonb (platform-specific data, e.g., expires_at, last_sync_at, wcVersion, wpVersion) - connected_at: timestamp (when store was connected) - is_active: boolean (default: true) - phone_number: text - package: text - sync_status: text ('idle', 'syncing', 'completed', 'error') - sync_started_at: timestamptz (when current/last sync started) - sync_completed_at: timestamptz (when last sync completed) - sync_error: text (error message from last failed sync) - created_at: timestamp - updated_at: timestamp ``` **oauth_states table** (for OAuth flow state management): ``` - id: UUID (primary key) - state: text (UUID for CSRF protection, unique) - user_id: UUID (FK to auth.users, nullable) - platform: text (e.g., 'shopify', 'woocommerce', 'shoprenter') - shopname: text (nullable) - expires_at: timestamptz - created_at: timestamptz ``` **pending_shoprenter_installs table** (temporary storage during OAuth): ``` - id: UUID (primary key) - installation_id: text (UUID, unique) - shopname: text - access_token: text - refresh_token: text (nullable) - token_type: text (nullable, default: 'Bearer') - expires_in: integer (nullable) - scopes: text[] (nullable) - expires_at: timestamptz - created_at: timestamptz ``` **oauth_nonces table** (stores OAuth nonce values for OAuth flows): ``` - id: UUID (primary key) - nonce: text (unique) - user_id: UUID (FK to auth.users, nullable) - platform: text - shop: text (nullable) - app_url: text (nullable) - shopname: text (nullable) - created_at: timestamptz - expires_at: timestamptz ``` **pending_signups table** (stores pending signup data with OTP for email verification): ``` - id: UUID (primary key) - signup_id: text (unique) - email: text - password: text - full_name: text - company_name: text - user_name: text - otp: text - created_at: timestamptz - expires_at: timestamptz (expires after 15 minutes) ``` **profiles table** (user profile information): ``` - id: UUID (primary key, FK to auth.users) - full_name: text (nullable) - username: text (nullable, unique) - email: text (nullable) - company_name: text (nullable) - is_verified: boolean (nullable) - created_at: timestamp (default: now()) - updated_at: timestamp (default: now()) ``` **shoprenter_products_cache table** (cached product data): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - shoprenter_product_id: varchar - product_data: jsonb - last_synced_at: timestamptz - created_at: timestamptz - updated_at: timestamptz ``` **shoprenter_tokens table** (ShopRenter token management): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - access_token: text - refresh_token: text (nullable) - expires_at: timestamptz (nullable) - scopes: text[] (nullable) - shopname: varchar - shop_domain: varchar - is_active: boolean (default: true) - last_sync_at: timestamptz (nullable) - created_at: timestamptz - updated_at: timestamptz ``` **shoprenter_webhooks table** (webhook registrations): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - webhook_id: varchar - topic: varchar - webhook_url: text - is_active: boolean (default: true) - created_at: timestamptz - updated_at: timestamptz ``` **woocommerce_products_cache table** (cached WooCommerce products): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - wc_product_id: text - name: text - sku: text - price: decimal - currency: text - description: text - short_description: text - stock_quantity: integer - stock_status: text - type: text ('simple', 'variable', 'grouped', 'external') - categories: jsonb - images: jsonb - raw_data: jsonb - last_synced_at: timestamptz - created_at: timestamptz - UNIQUE(store_id, wc_product_id) ``` **woocommerce_orders_cache table** (cached WooCommerce orders): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - wc_order_id: text - order_number: text - status: text - total: decimal - currency: text - customer_name: text - customer_email: text - line_items: jsonb - billing_address: jsonb - shipping_address: jsonb - created_at: timestamptz - raw_data: jsonb - last_synced_at: timestamptz - UNIQUE(store_id, wc_order_id) ``` **woocommerce_customers_cache table** (cached WooCommerce customers): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - wc_customer_id: text - email: text - first_name: text - last_name: text - username: text - billing_address: jsonb - shipping_address: jsonb - orders_count: integer - total_spent: decimal - raw_data: jsonb - last_synced_at: timestamptz - created_at: timestamptz - UNIQUE(store_id, wc_customer_id) ``` **shopify_products_cache table** (cached Shopify products): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - shopify_product_id: text - title: text - handle: text - vendor: text - product_type: text - status: text - price: decimal - compare_at_price: decimal - currency: text - sku: text - inventory_quantity: integer - description: text - images: jsonb - variants: jsonb - options: jsonb - tags: text[] - raw_data: jsonb - last_synced_at: timestamptz - created_at: timestamptz - UNIQUE(store_id, shopify_product_id) ``` **shopify_orders_cache table** (cached Shopify orders): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - shopify_order_id: text - order_number: text - name: text (e.g., "#1001") - email: text - phone: text - financial_status: text - fulfillment_status: text - total_price: decimal - subtotal_price: decimal - total_tax: decimal - currency: text - customer_name: text - customer_email: text - line_items: jsonb - billing_address: jsonb - shipping_address: jsonb - note: text - tags: text[] - order_created_at: timestamptz - order_updated_at: timestamptz - raw_data: jsonb - last_synced_at: timestamptz - created_at: timestamptz - UNIQUE(store_id, shopify_order_id) ``` **shopify_customers_cache table** (cached Shopify customers): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - shopify_customer_id: text - email: text - first_name: text - last_name: text - phone: text - accepts_marketing: boolean - orders_count: integer - total_spent: decimal - currency: text - state: text (enabled, disabled, declined, invited) - addresses: jsonb - default_address: jsonb - tags: text[] - note: text - customer_created_at: timestamptz - customer_updated_at: timestamptz - raw_data: jsonb - last_synced_at: timestamptz - created_at: timestamptz - UNIQUE(store_id, shopify_customer_id) ``` **gdpr_requests table** (GDPR compliance tracking): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - request_type: text ('data_request', 'customer_redact', 'shop_redact') - customer_id: text - shop_domain: text - request_payload: jsonb - status: text ('pending', 'processing', 'completed', 'failed') - processed_at: timestamptz - error_message: text - created_at: timestamptz - updated_at: timestamptz ``` **shopify_webhooks table** (webhook registration tracking): ``` - id: UUID (primary key) - store_id: UUID (FK to stores) - shopify_webhook_id: text - topic: text (e.g., 'orders/create', 'products/update') - address: text (webhook callback URL) - format: text (default: 'json') - is_active: boolean (default: true) - last_received_at: timestamptz (nullable) - total_received: integer (default: 0) - created_at: timestamptz - updated_at: timestamptz - UNIQUE(store_id, shopify_webhook_id) ``` **system_config table** (system-wide configuration): ``` - key: text (primary key) - value: text - description: text (nullable) - created_at: timestamptz - updated_at: timestamptz ``` **call_logs table** (call history and analytics): ``` - id: UUID (primary key) - user_id: UUID (FK to auth.users, nullable) - contact_id: UUID (nullable) - workspace_id: UUID - status: call_status_enum (pending, scheduled, in_progress, completed, failed, cancelled) - attempt_number: integer - scheduled_at: timestamptz (nullable) - started_at: timestamptz (nullable) - ended_at: timestamptz (nullable) - duration_seconds: integer (nullable) - transcript: text (nullable) - summary: text (nullable) - call_outcome: call_outcome_enum (pending, resolved, interested, potential, not_interested, no_answer, voicemail, busy, callback_requested, false) - assistant_id: text (nullable) - assistant_name: text (nullable) - callprovider_id: text (nullable) - phone_number_id: text (nullable) - customer_number: text (nullable) - assistant_phone_number: text (nullable) - call_type: call_type_enum (inbound, outbound, inbound_dummy) - cost_stt: numeric (nullable) - cost_llm: numeric (nullable) - cost_tts: numeric (nullable) - cost_total: numeric (nullable) - cost_twilio: numeric (nullable) - cost_totals: numeric (nullable) - ended_reason: text (nullable) - recording_url: text (nullable) - created_at: timestamptz - updated_at: timestamptz ``` **sync_logs table** (scheduled sync execution logs): ``` - id: UUID (primary key) - sync_type: text ('manual', 'scheduled', 'webhook') - platform: text ('shopify', 'woocommerce', 'shoprenter') - stores_processed: integer - results: jsonb (detailed results per store) - started_at: timestamptz - completed_at: timestamptz - created_at: timestamptz ``` **store_sync_config table** (per-store sync configuration): ``` - id: UUID (primary key) - store_id: UUID (FK to stores, unique) - enabled: boolean (default: true) - sync_frequency: text ('15min', '30min', 'hourly', '6hours', 'daily') - last_sync_at: timestamptz - next_sync_at: timestamptz (auto-calculated) - products_access_policy: data_access_policy enum ('sync', 'api_only', 'not_allowed') - GDPR-compliant access control - customers_access_policy: data_access_policy enum ('sync', 'api_only', 'not_allowed') - GDPR-compliant access control - orders_access_policy: data_access_policy enum ('sync', 'api_only', 'not_allowed') - GDPR-compliant access control - created_at: timestamptz - updated_at: timestamptz ``` ## Environment Configuration ### Quick Setup 1. **Frontend**: Copy `shopcall.ai-main/.env.example` to `.env` and fill in your values 2. **Backend**: Copy `supabase/.env.example` to `.env` and fill in your values 3. **Database**: Configure pg_cron settings in Supabase Dashboard (see below) ### Frontend `.env` (shopcall.ai-main) Required environment variables: ```bash # Supabase Configuration (from Supabase Dashboard → Settings → API) VITE_SUPABASE_URL=https://YOUR_PROJECT.supabase.co VITE_SUPABASE_ANON_KEY=your_supabase_anon_key # Backend API Base URL (typically VITE_SUPABASE_URL + /functions/v1) VITE_API_URL=https://YOUR_PROJECT.supabase.co/functions/v1 # Frontend URL (for OAuth callbacks) VITE_FRONTEND_URL=http://localhost:8080 # or https://yourdomain.com for production ``` **Note**: `VITE_API_URL` is derived from `VITE_SUPABASE_URL` by appending `/functions/v1` ### Supabase Edge Functions `.env` (supabase) Required environment variables: ```bash # Supabase Configuration (from Supabase Dashboard → Settings → API) SUPABASE_URL=https://YOUR_PROJECT.supabase.co SUPABASE_ANON_KEY=your_supabase_anon_key SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key # Frontend URL (same as VITE_FRONTEND_URL in frontend .env) FRONTEND_URL=http://localhost:8080 # or https://yourdomain.com for production # E-commerce Platform OAuth Credentials 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 # Internal Security (generate with: openssl rand -hex 32) INTERNAL_SYNC_SECRET=your_random_secure_secret # Email Configuration (from https://resend.com/api-keys) RESEND_API_KEY=your_resend_api_key ``` ### Supabase Database Settings (for pg_cron) **CRITICAL**: Configure in Supabase Dashboard → Project Settings → Database → Custom Postgres Configuration: ``` app.internal_sync_secret = 'same_as_INTERNAL_SYNC_SECRET_above' app.supabase_url = 'https://YOUR_PROJECT.supabase.co' ``` **Why this is required**: The pg_cron jobs run database-side trigger functions that need to make HTTP requests to Edge Functions. These settings allow the trigger functions to authenticate and call the scheduled sync Edge Functions. **Without these settings**: - pg_cron jobs will run successfully but silently fail - No sync will actually happen (appears "stuck") - No error logs will be visible **To verify settings are configured**: ```sql SELECT current_setting('app.internal_sync_secret', true) as internal_secret, current_setting('app.supabase_url', true) as supabase_url; ``` Both values should be non-NULL. ## Deployment ### 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 ### Supabase Edge Functions - **Deployment**: Use `supabase functions deploy` command - Edge Functions are deployed to Supabase infrastructure - Automatically scaled and managed by Supabase ### Using Supabase MCP Tools (Recommended for Claude) **IMPORTANT**: When working with Supabase in Claude Code, ALWAYS use the Supabase MCP tools instead of CLI commands. These tools provide direct integration with Supabase without requiring manual authentication. #### Available MCP Tools **Edge Functions Management:** - `mcp__supabase__list_edge_functions` - List all deployed Edge Functions - `mcp__supabase__deploy_edge_function` - Deploy an Edge Function (with files array) - `mcp__supabase__get_edge_function` - Get Edge Function code **Database Operations:** - `mcp__supabase__list_tables` - List database tables - `mcp__supabase__list_extensions` - List database extensions - `mcp__supabase__list_migrations` - List all migrations - `mcp__supabase__apply_migration` - Apply a new migration (DDL operations) - `mcp__supabase__execute_sql` - Execute raw SQL queries **Project Information:** - `mcp__supabase__get_project_url` - Get the project API URL - `mcp__supabase__get_anon_key` - Get the anonymous API key - `mcp__supabase__generate_typescript_types` - Generate TypeScript types from database **Monitoring & Debugging:** - `mcp__supabase__get_logs` - Get logs for Edge Functions or other services - `mcp__supabase__get_advisors` - Check for security/performance issues **Branching (Development):** - `mcp__supabase__create_branch` - Create a development branch - `mcp__supabase__list_branches` - List all branches - `mcp__supabase__merge_branch` - Merge branch to production - `mcp__supabase__delete_branch` - Delete a development branch #### Best Practices 1. **Always use MCP tools** for Supabase operations in Claude Code 2. **Check Edge Function logs** when debugging deployment issues: ``` mcp__supabase__get_logs(service: "edge-function") ``` 3. **Monitor deployments** by listing Edge Functions after deployment 4. **Environment variables** are configured via `.env` files in both frontend and supabase directories 5. **For production deployment**, ensure both `.env` files are properly configured #### Common Workflows **Deploying Edge Functions via CLI:** ```bash cd /home/claude/shopcall/supabase # Deploy a specific function ~/bin/supabase functions deploy --project-ref ztklqodcdjeqpsvhlpud # Deploy multiple functions ~/bin/supabase functions deploy api oauth-woocommerce oauth-shopify --project-ref ztklqodcdjeqpsvhlpud ``` **Checking Deployment Status:** Use the MCP tool `mcp__supabase__list_edge_functions` to verify deployment status and version numbers. **Debugging 404/500 Errors:** 1. Check if Edge Function is deployed using `mcp__supabase__list_edge_functions` 2. Review logs using `mcp__supabase__get_logs(service: "edge-function")` 3. Verify environment variables are set in `supabase/.env` 4. Redeploy the function if needed ## Development Workflow When making changes: 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 `` wrapper 5. **New API Endpoints**: Create new Edge Functions in `supabase/functions/` ### ⚠️ Critical: Deploying Edge Function Changes **IMPORTANT**: After modifying any Supabase Edge Function code, you MUST redeploy the function to Supabase for the changes to take effect! **Steps to deploy Edge Functions:** 1. **Via MCP Tools** (PREFERRED for Claude Code): ``` Use mcp__supabase__deploy_edge_function with: - name: function name (e.g., "trigger-sync", "api", "oauth-shopify") - files: array of files with name and content properties Example: mcp__supabase__deploy_edge_function( name: "trigger-sync", files: [{"name": "index.ts", "content": "..."}] ) ``` **Benefits**: No manual authentication, automatic project selection, works directly in Claude Code 2. **Via CLI** (alternative, requires manual authentication): ```bash cd /home/claude/shopcall/supabase # Deploy a specific function ~/bin/supabase functions deploy --project-ref ztklqodcdjeqpsvhlpud # Deploy multiple functions at once ~/bin/supabase functions deploy oauth-woocommerce oauth-shopify api --project-ref ztklqodcdjeqpsvhlpud ``` 3. **Verify deployment**: - Check version number increased using `mcp__supabase__list_edge_functions` - Review logs using `mcp__supabase__get_logs(service: "edge-function")` - Test the endpoint to ensure changes are live **Common mistake**: Forgetting to redeploy after code changes means the old version continues running in production! **Which functions to deploy:** - If you modified a function file directly (e.g., `oauth-woocommerce/index.ts`), deploy that function - If you modified a shared file (e.g., `_shared/woocommerce-client.ts`), deploy ALL functions that import it - When in doubt, check which functions import the modified file and deploy all of them ## Important Notes - 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 Frontend uses TypeScript path alias: - `@/` → `./src/` Example: `import { Button } from "@/components/ui/button"` ## Background Sync & Scheduling ### Automated Synchronization for E-commerce Platforms The project includes automated background sync capabilities for ShopRenter and WooCommerce using PostgreSQL's `pg_cron` extension. Shopify scheduled sync is prepared in the migration but not yet implemented. **How it works**: 1. **pg_cron** schedules database jobs at specified intervals 2. **pg_net** makes HTTP requests from the database to Edge Functions 3. **Platform-specific scheduled-sync Edge Functions** process all active stores 4. Sync results are logged to `sync_logs` table **Implemented Scheduled Syncs**: - **ShopRenter**: `shoprenter-scheduled-sync` (runs hourly at minute 0) - **WooCommerce**: `woocommerce-scheduled-sync` (runs hourly at minute 5) - **Shopify**: Migration prepared, Edge Function not yet deployed **Sync Frequencies** (configurable per store in `store_sync_config`): - `15min` - Every 15 minutes (high-frequency updates) - `30min` - Every 30 minutes - `hourly` - Every hour (default, recommended) - `6hours` - Every 6 hours - `daily` - Once per day **Configuration**: - Per-store sync frequency via `store_sync_config` table - Enable/disable sync per store - Choose what to sync (products, orders, customers) - Automatic next sync calculation - Default sync config auto-created when connecting new stores **Monitoring**: - `sync_logs` table tracks all sync executions - Logs include per-store success/failure status - Sync duration and item counts tracked - Stores table has `sync_status`, `sync_started_at`, `sync_completed_at`, `sync_error` fields **Setup Requirements**: 1. Run migrations: - `supabase/migrations/20250129_shoprenter_scheduled_sync.sql` - `supabase/migrations/20251030_woocommerce_scheduled_sync.sql` - `supabase/migrations/20251030_shopify_integration.sql` 2. Deploy Edge Functions: - `supabase functions deploy shoprenter-scheduled-sync` - `supabase functions deploy woocommerce-scheduled-sync` - `supabase functions deploy shopify-scheduled-sync` (when implemented) 3. Configure `INTERNAL_SYNC_SECRET` in Edge Functions environment 4. Configure database settings in Supabase Dashboard (see Environment Configuration section) 5. Monitor `sync_logs` table for execution results **Manual Control**: ```sql -- Enable/disable sync for a store SELECT set_store_sync_enabled('store-uuid', true); -- Change sync frequency SELECT set_store_sync_frequency('store-uuid', 'hourly'); -- View recent sync logs SELECT * FROM sync_logs ORDER BY created_at DESC LIMIT 10; -- View sync statistics (if view exists) SELECT * FROM sync_statistics; -- Check current pg_cron jobs SELECT * FROM cron.job; ``` **Security**: - `INTERNAL_SYNC_SECRET` prevents unauthorized sync triggers - Only internal pg_cron jobs can trigger scheduled sync - Row-level security ensures users only see their own sync logs **Trigger Functions**: - `trigger_shoprenter_scheduled_sync()` - Triggers ShopRenter sync - `trigger_woocommerce_scheduled_sync()` - Triggers WooCommerce sync - `trigger_shopify_scheduled_sync()` - Triggers Shopify sync (when implemented)