|
|
@@ -23,7 +23,8 @@ import {
|
|
|
createProductText,
|
|
|
createOrderText,
|
|
|
createCustomerText,
|
|
|
- QdrantPoint
|
|
|
+ QdrantPoint,
|
|
|
+ QdrantConfig
|
|
|
} from '../_shared/qdrant-client.ts'
|
|
|
|
|
|
const corsHeaders = {
|
|
|
@@ -104,7 +105,8 @@ async function syncProductsToQdrant(
|
|
|
storeId: string,
|
|
|
storeName: string,
|
|
|
products: ShopifyProduct[],
|
|
|
- supabaseAdmin: any
|
|
|
+ supabaseAdmin: any,
|
|
|
+ config?: QdrantConfig
|
|
|
): Promise<{ synced: number; errors: number }> {
|
|
|
const startTime = new Date()
|
|
|
const collectionName = getCollectionName(storeName, 'products')
|
|
|
@@ -116,7 +118,7 @@ async function syncProductsToQdrant(
|
|
|
|
|
|
try {
|
|
|
// Ensure collection exists
|
|
|
- if (!(await collectionExists(collectionName))) {
|
|
|
+ if (!(await collectionExists(collectionName, config))) {
|
|
|
await createCollection(collectionName, [
|
|
|
{ field: 'store_id', type: 'keyword' },
|
|
|
{ field: 'product_id', type: 'keyword' },
|
|
|
@@ -124,13 +126,13 @@ async function syncProductsToQdrant(
|
|
|
{ field: 'status', type: 'keyword' },
|
|
|
{ field: 'price', type: 'float' },
|
|
|
{ field: 'sku', type: 'keyword' },
|
|
|
- ])
|
|
|
+ ], config)
|
|
|
}
|
|
|
|
|
|
// Get existing product IDs from Qdrant to detect deletions
|
|
|
const existingPoints = await scrollPoints(collectionName, {
|
|
|
must: [{ key: 'store_id', match: { value: storeId } }]
|
|
|
- }, 1000)
|
|
|
+ }, 1000, config)
|
|
|
|
|
|
const existingProductIds = new Set(
|
|
|
existingPoints.points.map((p: any) => p.payload?.product_id).filter(Boolean)
|
|
|
@@ -152,7 +154,7 @@ async function syncProductsToQdrant(
|
|
|
{ key: 'store_id', match: { value: storeId } },
|
|
|
{ key: 'product_id', match: { any: deletedProductIds } }
|
|
|
]
|
|
|
- })
|
|
|
+ }, config)
|
|
|
}
|
|
|
|
|
|
// Generate text representations for all products
|
|
|
@@ -256,7 +258,7 @@ async function syncProductsToQdrant(
|
|
|
})
|
|
|
|
|
|
// Upsert to Qdrant
|
|
|
- await upsertPoints(collectionName, points)
|
|
|
+ await upsertPoints(collectionName, points, config)
|
|
|
synced = points.length
|
|
|
|
|
|
await logQdrantSync(
|
|
|
@@ -297,7 +299,8 @@ async function syncOrdersToQdrant(
|
|
|
storeId: string,
|
|
|
storeName: string,
|
|
|
orders: ShopifyOrder[],
|
|
|
- supabaseAdmin: any
|
|
|
+ supabaseAdmin: any,
|
|
|
+ config?: QdrantConfig
|
|
|
): Promise<{ synced: number; errors: number }> {
|
|
|
const startTime = new Date()
|
|
|
const collectionName = getCollectionName(storeName, 'orders')
|
|
|
@@ -308,7 +311,7 @@ async function syncOrdersToQdrant(
|
|
|
let errors = 0
|
|
|
|
|
|
try {
|
|
|
- if (!(await collectionExists(collectionName))) {
|
|
|
+ if (!(await collectionExists(collectionName, config))) {
|
|
|
await createCollection(collectionName, [
|
|
|
{ field: 'store_id', type: 'keyword' },
|
|
|
{ field: 'order_id', type: 'keyword' },
|
|
|
@@ -316,7 +319,7 @@ async function syncOrdersToQdrant(
|
|
|
{ field: 'financial_status', type: 'keyword' },
|
|
|
{ field: 'total_price', type: 'float' },
|
|
|
{ field: 'customer_email', type: 'keyword' },
|
|
|
- ])
|
|
|
+ ], config)
|
|
|
}
|
|
|
|
|
|
// Generate text representations for all orders
|
|
|
@@ -375,7 +378,7 @@ async function syncOrdersToQdrant(
|
|
|
}
|
|
|
}))
|
|
|
|
|
|
- await upsertPoints(collectionName, points)
|
|
|
+ await upsertPoints(collectionName, points, config)
|
|
|
synced = points.length
|
|
|
|
|
|
await logQdrantSync(
|
|
|
@@ -416,7 +419,8 @@ async function syncCustomersToQdrant(
|
|
|
storeId: string,
|
|
|
storeName: string,
|
|
|
customers: ShopifyCustomer[],
|
|
|
- supabaseAdmin: any
|
|
|
+ supabaseAdmin: any,
|
|
|
+ config?: QdrantConfig
|
|
|
): Promise<{ synced: number; errors: number }> {
|
|
|
const startTime = new Date()
|
|
|
const collectionName = getCollectionName(storeName, 'customers')
|
|
|
@@ -427,13 +431,13 @@ async function syncCustomersToQdrant(
|
|
|
let errors = 0
|
|
|
|
|
|
try {
|
|
|
- if (!(await collectionExists(collectionName))) {
|
|
|
+ if (!(await collectionExists(collectionName, config))) {
|
|
|
await createCollection(collectionName, [
|
|
|
{ field: 'store_id', type: 'keyword' },
|
|
|
{ field: 'customer_id', type: 'keyword' },
|
|
|
{ field: 'platform', type: 'keyword' },
|
|
|
{ field: 'email', type: 'keyword' },
|
|
|
- ])
|
|
|
+ ], config)
|
|
|
}
|
|
|
|
|
|
// Generate text representations for all customers
|
|
|
@@ -485,7 +489,7 @@ async function syncCustomersToQdrant(
|
|
|
}
|
|
|
}))
|
|
|
|
|
|
- await upsertPoints(collectionName, points)
|
|
|
+ await upsertPoints(collectionName, points, config)
|
|
|
synced = points.length
|
|
|
|
|
|
await logQdrantSync(
|
|
|
@@ -527,7 +531,8 @@ async function syncProducts(
|
|
|
storeName: string,
|
|
|
supabaseAdmin: any,
|
|
|
qdrantEnabled: boolean,
|
|
|
- canSyncProducts: boolean
|
|
|
+ canSyncProducts: boolean,
|
|
|
+ qdrantConfig?: QdrantConfig
|
|
|
): Promise<{ synced: number; errors: number; qdrant?: { synced: number; errors: number } }> {
|
|
|
console.log('[Shopify] Syncing products...')
|
|
|
let synced = 0
|
|
|
@@ -666,7 +671,7 @@ async function syncProducts(
|
|
|
}
|
|
|
|
|
|
// Sync remaining (non-excluded) products to Qdrant
|
|
|
- qdrantResult = await syncProductsToQdrant(storeId, storeName, productsForQdrant, supabaseAdmin)
|
|
|
+ qdrantResult = await syncProductsToQdrant(storeId, storeName, productsForQdrant, supabaseAdmin, qdrantConfig)
|
|
|
console.log(`[Shopify] Qdrant sync complete: ${qdrantResult.synced} synced, ${qdrantResult.errors} errors, ${excludedProductIds.length} excluded`)
|
|
|
}
|
|
|
|
|
|
@@ -685,7 +690,8 @@ async function syncOrders(
|
|
|
supabaseAdmin: any,
|
|
|
countryCode: string,
|
|
|
qdrantEnabled: boolean,
|
|
|
- canSyncOrders: boolean
|
|
|
+ canSyncOrders: boolean,
|
|
|
+ qdrantConfig?: QdrantConfig
|
|
|
): Promise<{ synced: number; errors: number; qdrant?: { synced: number; errors: number } }> {
|
|
|
console.log('[Shopify] Syncing orders...')
|
|
|
let synced = 0
|
|
|
@@ -760,7 +766,7 @@ async function syncOrders(
|
|
|
// Sync to Qdrant if enabled
|
|
|
let qdrantResult
|
|
|
if (qdrantEnabled) {
|
|
|
- qdrantResult = await syncOrdersToQdrant(storeId, storeName, orders, supabaseAdmin)
|
|
|
+ qdrantResult = await syncOrdersToQdrant(storeId, storeName, orders, supabaseAdmin, qdrantConfig)
|
|
|
}
|
|
|
|
|
|
return { synced, errors, qdrant: qdrantResult }
|
|
|
@@ -778,7 +784,8 @@ async function syncCustomers(
|
|
|
supabaseAdmin: any,
|
|
|
countryCode: string,
|
|
|
qdrantEnabled: boolean,
|
|
|
- canSyncCustomers: boolean
|
|
|
+ canSyncCustomers: boolean,
|
|
|
+ qdrantConfig?: QdrantConfig
|
|
|
): Promise<{ synced: number; errors: number; qdrant?: { synced: number; errors: number } }> {
|
|
|
console.log('[Shopify] Syncing customers...')
|
|
|
let synced = 0
|
|
|
@@ -848,7 +855,7 @@ async function syncCustomers(
|
|
|
// Sync to Qdrant if enabled
|
|
|
let qdrantResult
|
|
|
if (qdrantEnabled) {
|
|
|
- qdrantResult = await syncCustomersToQdrant(storeId, storeName, customers, supabaseAdmin)
|
|
|
+ qdrantResult = await syncCustomersToQdrant(storeId, storeName, customers, supabaseAdmin, qdrantConfig)
|
|
|
}
|
|
|
|
|
|
return { synced, errors, qdrant: qdrantResult }
|
|
|
@@ -905,7 +912,7 @@ serve(wrapHandler('shopify-sync', async (req) => {
|
|
|
|
|
|
const { data: store, error: storeError } = await supabaseAdmin
|
|
|
.from('stores')
|
|
|
- .select('id, platform_name, store_name, store_url, qdrant_sync_enabled')
|
|
|
+ .select('id, platform_name, store_name, store_url, qdrant_sync_enabled, qdrant_url, qdrant_api_key')
|
|
|
.eq('id', storeId)
|
|
|
.eq('user_id', user.id)
|
|
|
.eq('platform_name', 'shopify')
|
|
|
@@ -932,6 +939,12 @@ serve(wrapHandler('shopify-sync', async (req) => {
|
|
|
const canSyncCustomers = syncConfig?.customers_access_policy === 'sync'
|
|
|
const qdrantEnabled = store.qdrant_sync_enabled !== false
|
|
|
|
|
|
+ // Prepare Qdrant configuration (per-store override or defaults from environment)
|
|
|
+ const qdrantConfig = (store.qdrant_url || store.qdrant_api_key) ? {
|
|
|
+ url: store.qdrant_url || undefined,
|
|
|
+ apiKey: store.qdrant_api_key || undefined,
|
|
|
+ } : undefined
|
|
|
+
|
|
|
console.log('[Shopify] Sync permissions:', {
|
|
|
products: canSyncProducts,
|
|
|
productsPolicy: syncConfig?.products_access_policy || 'not_configured',
|
|
|
@@ -939,7 +952,8 @@ serve(wrapHandler('shopify-sync', async (req) => {
|
|
|
ordersPolicy: syncConfig?.orders_access_policy || 'not_configured',
|
|
|
customers: canSyncCustomers,
|
|
|
customersPolicy: syncConfig?.customers_access_policy || 'not_configured',
|
|
|
- qdrant: qdrantEnabled
|
|
|
+ qdrant: qdrantEnabled,
|
|
|
+ qdrantCustomConfig: qdrantConfig ? 'yes' : 'no (using defaults)'
|
|
|
})
|
|
|
|
|
|
// Initialize Qdrant collections if enabled
|
|
|
@@ -948,7 +962,8 @@ serve(wrapHandler('shopify-sync', async (req) => {
|
|
|
await initializeStoreCollections(
|
|
|
store.store_name,
|
|
|
canSyncOrders,
|
|
|
- canSyncCustomers
|
|
|
+ canSyncCustomers,
|
|
|
+ qdrantConfig
|
|
|
)
|
|
|
} catch (error) {
|
|
|
console.error('[Qdrant] Failed to initialize collections:', error)
|
|
|
@@ -970,15 +985,15 @@ serve(wrapHandler('shopify-sync', async (req) => {
|
|
|
|
|
|
// Perform sync based on type
|
|
|
if (syncType === 'all' || syncType === 'products') {
|
|
|
- results.products = await syncProducts(storeId, store.store_name, supabaseAdmin, qdrantEnabled, canSyncProducts)
|
|
|
+ results.products = await syncProducts(storeId, store.store_name, supabaseAdmin, qdrantEnabled, canSyncProducts, qdrantConfig)
|
|
|
}
|
|
|
|
|
|
if (syncType === 'all' || syncType === 'orders') {
|
|
|
- results.orders = await syncOrders(storeId, store.store_name, supabaseAdmin, countryCode, qdrantEnabled, canSyncOrders)
|
|
|
+ results.orders = await syncOrders(storeId, store.store_name, supabaseAdmin, countryCode, qdrantEnabled, canSyncOrders, qdrantConfig)
|
|
|
}
|
|
|
|
|
|
if (syncType === 'all' || syncType === 'customers') {
|
|
|
- results.customers = await syncCustomers(storeId, store.store_name, supabaseAdmin, countryCode, qdrantEnabled, canSyncCustomers)
|
|
|
+ results.customers = await syncCustomers(storeId, store.store_name, supabaseAdmin, countryCode, qdrantEnabled, canSyncCustomers, qdrantConfig)
|
|
|
}
|
|
|
|
|
|
results.completed_at = new Date().toISOString()
|