/** * Run Jane product discovery for stores in database * Usage: npx ts-node scripts/run-jane-product-discovery.ts [DISPENSARY_ID] * Example: npx ts-node scripts/run-jane-product-discovery.ts 4220 * Or run for all Jane stores: npx ts-node scripts/run-jane-product-discovery.ts all */ import { Pool } from 'pg'; import { fetchProductsByStoreIdDirect } from '../src/platforms/jane'; import { saveRawPayload } from '../src/utils/payload-storage'; async function main() { const arg = process.argv[2]; console.log('='.repeat(60)); console.log('Jane Product Discovery'); console.log('='.repeat(60)); const pool = new Pool({ connectionString: process.env.DATABASE_URL, }); try { // Get dispensaries to process let dispensaries: any[]; if (arg === 'all') { const result = await pool.query( `SELECT id, name, menu_url, platform_dispensary_id FROM dispensaries WHERE platform = 'jane' AND menu_url IS NOT NULL ORDER BY id` ); dispensaries = result.rows; } else if (arg) { const result = await pool.query( `SELECT id, name, menu_url, platform_dispensary_id FROM dispensaries WHERE id = $1`, [parseInt(arg)] ); dispensaries = result.rows; } else { // Default: get first Jane store const result = await pool.query( `SELECT id, name, menu_url, platform_dispensary_id FROM dispensaries WHERE platform = 'jane' AND menu_url IS NOT NULL ORDER BY id LIMIT 1` ); dispensaries = result.rows; } if (dispensaries.length === 0) { console.log('No Jane dispensaries found'); return; } console.log(`Processing ${dispensaries.length} dispensary(ies)...\n`); let successCount = 0; let failCount = 0; for (const disp of dispensaries) { console.log(`\n${'─'.repeat(60)}`); console.log(`${disp.name} (ID: ${disp.id}, Jane ID: ${disp.platform_dispensary_id})`); console.log('─'.repeat(60)); try { const result = await fetchProductsByStoreIdDirect(disp.platform_dispensary_id); if (result.products.length === 0) { console.log(' ✗ No products captured'); failCount++; continue; } console.log(` ✓ Captured ${result.products.length} products`); // Build payload const rawPayload = { hits: result.products.map(p => p.raw), store: result.store?.raw || null, capturedAt: new Date().toISOString(), platform: 'jane', dispensaryId: disp.id, storeId: disp.platform_dispensary_id, }; // Save payload const { id: payloadId, sizeBytes } = await saveRawPayload( pool, disp.id, rawPayload, null, result.products.length, 'jane' ); console.log(` ✓ Saved payload ${payloadId} (${Math.round(sizeBytes / 1024)}KB)`); // Update dispensary await pool.query( `UPDATE dispensaries SET stage = 'hydrating', last_fetch_at = NOW(), product_count = $2, consecutive_successes = consecutive_successes + 1, consecutive_failures = 0, updated_at = NOW() WHERE id = $1`, [disp.id, result.products.length] ); console.log(` ✓ Updated dispensary (product_count: ${result.products.length})`); successCount++; } catch (error: any) { console.log(` ✗ Error: ${error.message}`); failCount++; } } console.log('\n' + '='.repeat(60)); console.log('RESULTS'); console.log('='.repeat(60)); console.log(`Success: ${successCount}`); console.log(`Failed: ${failCount}`); } catch (error: any) { console.error('Error:', error.message); process.exit(1); } finally { await pool.end(); } } main();