|
@@ -112,83 +112,169 @@ supabase functions serve
|
|
|
- GDPR webhooks: `webhooks-shopify` (customers/data_request, customers/redact, shop/redact)
|
|
- GDPR webhooks: `webhooks-shopify` (customers/data_request, customers/redact, shop/redact)
|
|
|
- API client: `_shared/shopify-client.ts`
|
|
- API client: `_shared/shopify-client.ts`
|
|
|
- **Data synchronization**: `shopify-sync` (manual sync for products, orders, customers)
|
|
- **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
|
|
- 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
|
|
- Secure OAuth 2.0 with HMAC-SHA256 signature validation
|
|
|
- Rate limiting (5 req/sec) and retry logic with exponential backoff
|
|
- Rate limiting (5 req/sec) and retry logic with exponential backoff
|
|
|
- Automatic pagination support for all data types
|
|
- Automatic pagination support for all data types
|
|
|
- **Shopify App Store compliant** - includes mandatory GDPR webhooks
|
|
- **Shopify App Store compliant** - includes mandatory GDPR webhooks
|
|
|
|
|
+ - API Version: Shopify Admin API 2024-01
|
|
|
- **WooCommerce integration** (fully implemented)
|
|
- **WooCommerce integration** (fully implemented)
|
|
|
- - OAuth flow: `oauth-woocommerce` (OAuth 1.0a authentication)
|
|
|
|
|
|
|
+ - **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`
|
|
- API client: `_shared/woocommerce-client.ts`
|
|
|
- **Data synchronization**: `woocommerce-sync` (manual sync for products, orders, customers)
|
|
- **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/`)
|
|
- **API Version**: WooCommerce REST API v3 (`/wp-json/wc/v3/`)
|
|
|
- **IMPORTANT**: Uses latest WooCommerce REST API v3 (NOT legacy API)
|
|
- **IMPORTANT**: Uses latest WooCommerce REST API v3 (NOT legacy API)
|
|
|
- Official documentation: https://woocommerce.github.io/woocommerce-rest-api-docs
|
|
- Official documentation: https://woocommerce.github.io/woocommerce-rest-api-docs
|
|
|
- Requires: WooCommerce 3.5+ and WordPress 4.4+
|
|
- Requires: WooCommerce 3.5+ and WordPress 4.4+
|
|
|
- Legacy API versions (v1, v2, v3 at `/wc-api/v*`) are deprecated and NOT supported
|
|
- Legacy API versions (v1, v2, v3 at `/wc-api/v*`) are deprecated and NOT supported
|
|
|
- Read-only access to products, orders, and customers
|
|
- Read-only access to products, orders, and customers
|
|
|
- - Secure OAuth 1.0a with HMAC-SHA256 signatures
|
|
|
|
|
|
|
+ - Secure OAuth 1.0a with HMAC-SHA256 signatures for API requests
|
|
|
- Rate limiting (5 req/sec) and retry logic with exponential backoff
|
|
- Rate limiting (5 req/sec) and retry logic with exponential backoff
|
|
|
- Pagination support for large datasets
|
|
- Pagination support for large datasets
|
|
|
|
|
+ - Connection test validates credentials before storing
|
|
|
- **ShopRenter integration** (fully implemented)
|
|
- **ShopRenter integration** (fully implemented)
|
|
|
- OAuth flow: `oauth-shoprenter-init`, `oauth-shoprenter-callback`
|
|
- OAuth flow: `oauth-shoprenter-init`, `oauth-shoprenter-callback`
|
|
|
- Uninstall webhook: `webhook-shoprenter-uninstall`
|
|
- Uninstall webhook: `webhook-shoprenter-uninstall`
|
|
|
- Data sync endpoints: `shoprenter-products`, `shoprenter-orders`, `shoprenter-customers`
|
|
- Data sync endpoints: `shoprenter-products`, `shoprenter-orders`, `shoprenter-customers`
|
|
|
- Manual sync: `shoprenter-sync`
|
|
- Manual sync: `shoprenter-sync`
|
|
|
- - **Scheduled background sync**: `shoprenter-scheduled-sync` (automated via pg_cron)
|
|
|
|
|
|
|
+ - **Scheduled background sync**: `shoprenter-scheduled-sync` (automated via pg_cron, hourly)
|
|
|
- API client: `_shared/shoprenter-client.ts`
|
|
- API client: `_shared/shoprenter-client.ts`
|
|
|
|
|
+ - Token refresh: Automatic access token refresh when expired
|
|
|
- Store credentials in Supabase `stores` table
|
|
- Store credentials in Supabase `stores` table
|
|
|
|
|
|
|
|
### Database Schema (Supabase)
|
|
### Database Schema (Supabase)
|
|
|
|
|
|
|
|
**stores table**:
|
|
**stores table**:
|
|
|
```
|
|
```
|
|
|
-- user_id: UUID (FK to auth.users)
|
|
|
|
|
|
|
+- id: UUID (primary key)
|
|
|
|
|
+- user_id: UUID (FK to profiles.id)
|
|
|
- platform_name: text (e.g., 'shopify', 'woocommerce', 'shoprenter')
|
|
- platform_name: text (e.g., 'shopify', 'woocommerce', 'shoprenter')
|
|
|
- store_name: text
|
|
- store_name: text
|
|
|
- store_url: text
|
|
- store_url: text
|
|
|
-- api_key: text (access token for ShopRenter)
|
|
|
|
|
-- api_secret: text (refresh token for ShopRenter)
|
|
|
|
|
|
|
+- 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[]
|
|
- scopes: text[]
|
|
|
-- alt_data: jsonb (platform-specific data, e.g., expires_at, last_sync_at)
|
|
|
|
|
|
|
+- 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
|
|
- phone_number: text
|
|
|
- package: 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):
|
|
**oauth_states table** (for OAuth flow state management):
|
|
|
```
|
|
```
|
|
|
-- state: text (UUID for CSRF protection)
|
|
|
|
|
-- user_id: UUID (FK to auth.users)
|
|
|
|
|
-- platform: text (e.g., 'shoprenter')
|
|
|
|
|
-- shopname: text
|
|
|
|
|
-- expires_at: timestamp
|
|
|
|
|
|
|
+- 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):
|
|
**pending_shoprenter_installs table** (temporary storage during OAuth):
|
|
|
```
|
|
```
|
|
|
-- installation_id: text (UUID)
|
|
|
|
|
|
|
+- id: UUID (primary key)
|
|
|
|
|
+- installation_id: text (UUID, unique)
|
|
|
- shopname: text
|
|
- shopname: text
|
|
|
- access_token: text
|
|
- access_token: text
|
|
|
-- refresh_token: text
|
|
|
|
|
-- token_type: text
|
|
|
|
|
-- expires_in: integer
|
|
|
|
|
-- scopes: text[]
|
|
|
|
|
-- expires_at: timestamp
|
|
|
|
|
|
|
+- 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):
|
|
**shoprenter_products_cache table** (cached product data):
|
|
|
```
|
|
```
|
|
|
|
|
+- id: UUID (primary key)
|
|
|
- store_id: UUID (FK to stores)
|
|
- store_id: UUID (FK to stores)
|
|
|
-- shoprenter_product_id: text
|
|
|
|
|
-- name: text
|
|
|
|
|
-- sku: text
|
|
|
|
|
-- price: decimal
|
|
|
|
|
-- currency: text
|
|
|
|
|
-- description: text
|
|
|
|
|
-- stock: integer
|
|
|
|
|
-- active: boolean
|
|
|
|
|
-- raw_data: jsonb
|
|
|
|
|
-- last_synced_at: timestamp
|
|
|
|
|
|
|
+- 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):
|
|
**woocommerce_products_cache table** (cached WooCommerce products):
|
|
@@ -357,23 +443,55 @@ supabase functions serve
|
|
|
- topic: text (e.g., 'orders/create', 'products/update')
|
|
- topic: text (e.g., 'orders/create', 'products/update')
|
|
|
- address: text (webhook callback URL)
|
|
- address: text (webhook callback URL)
|
|
|
- format: text (default: 'json')
|
|
- format: text (default: 'json')
|
|
|
-- is_active: boolean
|
|
|
|
|
-- last_received_at: timestamptz
|
|
|
|
|
-- total_received: integer
|
|
|
|
|
|
|
+- is_active: boolean (default: true)
|
|
|
|
|
+- last_received_at: timestamptz (nullable)
|
|
|
|
|
+- total_received: integer (default: 0)
|
|
|
- created_at: timestamptz
|
|
- created_at: timestamptz
|
|
|
- updated_at: timestamptz
|
|
- updated_at: timestamptz
|
|
|
- UNIQUE(store_id, shopify_webhook_id)
|
|
- UNIQUE(store_id, shopify_webhook_id)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**shoprenter_webhooks table** (webhook registrations):
|
|
|
|
|
|
|
+**system_config table** (system-wide configuration):
|
|
|
```
|
|
```
|
|
|
-- store_id: UUID (FK to stores)
|
|
|
|
|
-- shoprenter_webhook_id: text
|
|
|
|
|
-- event: text (e.g., 'order/create')
|
|
|
|
|
-- callback_url: text
|
|
|
|
|
-- is_active: boolean
|
|
|
|
|
-- last_received_at: timestamp
|
|
|
|
|
-- total_received: integer
|
|
|
|
|
|
|
+- 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):
|
|
**sync_logs table** (scheduled sync execution logs):
|
|
@@ -623,17 +741,22 @@ Example: `import { Button } from "@/components/ui/button"`
|
|
|
|
|
|
|
|
## Background Sync & Scheduling
|
|
## Background Sync & Scheduling
|
|
|
|
|
|
|
|
-### ShopRenter Automated Synchronization
|
|
|
|
|
|
|
+### Automated Synchronization for E-commerce Platforms
|
|
|
|
|
|
|
|
-The ShopRenter integration includes automated background sync capabilities using PostgreSQL's `pg_cron` extension.
|
|
|
|
|
|
|
+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**:
|
|
**How it works**:
|
|
|
1. **pg_cron** schedules database jobs at specified intervals
|
|
1. **pg_cron** schedules database jobs at specified intervals
|
|
|
2. **pg_net** makes HTTP requests from the database to Edge Functions
|
|
2. **pg_net** makes HTTP requests from the database to Edge Functions
|
|
|
-3. **shoprenter-scheduled-sync** Edge Function processes all active stores
|
|
|
|
|
|
|
+3. **Platform-specific scheduled-sync Edge Functions** process all active stores
|
|
|
4. Sync results are logged to `sync_logs` table
|
|
4. Sync results are logged to `sync_logs` table
|
|
|
|
|
|
|
|
-**Sync Frequencies**:
|
|
|
|
|
|
|
+**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)
|
|
- `15min` - Every 15 minutes (high-frequency updates)
|
|
|
- `30min` - Every 30 minutes
|
|
- `30min` - Every 30 minutes
|
|
|
- `hourly` - Every hour (default, recommended)
|
|
- `hourly` - Every hour (default, recommended)
|
|
@@ -645,18 +768,25 @@ The ShopRenter integration includes automated background sync capabilities using
|
|
|
- Enable/disable sync per store
|
|
- Enable/disable sync per store
|
|
|
- Choose what to sync (products, orders, customers)
|
|
- Choose what to sync (products, orders, customers)
|
|
|
- Automatic next sync calculation
|
|
- Automatic next sync calculation
|
|
|
|
|
+- Default sync config auto-created when connecting new stores
|
|
|
|
|
|
|
|
**Monitoring**:
|
|
**Monitoring**:
|
|
|
- `sync_logs` table tracks all sync executions
|
|
- `sync_logs` table tracks all sync executions
|
|
|
-- `sync_statistics` view provides aggregated metrics
|
|
|
|
|
- Logs include per-store success/failure status
|
|
- Logs include per-store success/failure status
|
|
|
- Sync duration and item counts tracked
|
|
- Sync duration and item counts tracked
|
|
|
|
|
+- Stores table has `sync_status`, `sync_started_at`, `sync_completed_at`, `sync_error` fields
|
|
|
|
|
|
|
|
**Setup Requirements**:
|
|
**Setup Requirements**:
|
|
|
-1. Run migration: `supabase/migrations/20250129_shoprenter_scheduled_sync.sql`
|
|
|
|
|
-2. Deploy Edge Function: `supabase functions deploy shoprenter-scheduled-sync`
|
|
|
|
|
|
|
+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
|
|
3. Configure `INTERNAL_SYNC_SECRET` in Edge Functions environment
|
|
|
-4. Configure database settings in Supabase Dashboard
|
|
|
|
|
|
|
+4. Configure database settings in Supabase Dashboard (see Environment Configuration section)
|
|
|
5. Monitor `sync_logs` table for execution results
|
|
5. Monitor `sync_logs` table for execution results
|
|
|
|
|
|
|
|
**Manual Control**:
|
|
**Manual Control**:
|
|
@@ -670,11 +800,19 @@ SELECT set_store_sync_frequency('store-uuid', 'hourly');
|
|
|
-- View recent sync logs
|
|
-- View recent sync logs
|
|
|
SELECT * FROM sync_logs ORDER BY created_at DESC LIMIT 10;
|
|
SELECT * FROM sync_logs ORDER BY created_at DESC LIMIT 10;
|
|
|
|
|
|
|
|
--- View sync statistics
|
|
|
|
|
|
|
+-- View sync statistics (if view exists)
|
|
|
SELECT * FROM sync_statistics;
|
|
SELECT * FROM sync_statistics;
|
|
|
|
|
+
|
|
|
|
|
+-- Check current pg_cron jobs
|
|
|
|
|
+SELECT * FROM cron.job;
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
**Security**:
|
|
**Security**:
|
|
|
- `INTERNAL_SYNC_SECRET` prevents unauthorized sync triggers
|
|
- `INTERNAL_SYNC_SECRET` prevents unauthorized sync triggers
|
|
|
- Only internal pg_cron jobs can trigger scheduled sync
|
|
- Only internal pg_cron jobs can trigger scheduled sync
|
|
|
- Row-level security ensures users only see their own sync logs
|
|
- 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)
|