"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const express_1 = require("express"); const middleware_1 = require("../auth/middleware"); const migrate_1 = require("../db/migrate"); const router = (0, express_1.Router)(); router.use(middleware_1.authMiddleware); // Valid menu_type values const VALID_MENU_TYPES = ['dutchie', 'treez', 'jane', 'weedmaps', 'leafly', 'meadow', 'blaze', 'flowhub', 'dispense', 'cova', 'other', 'unknown']; // Get all dispensaries router.get('/', async (req, res) => { try { const { menu_type } = req.query; let query = ` SELECT id, azdhs_id, name, company_name, slug, address, city, state, zip, phone, email, website, dba_name, google_rating, google_review_count, status_line, azdhs_url, latitude, longitude, menu_url, menu_type, menu_provider, menu_provider_confidence, scraper_template, last_menu_scrape, menu_scrape_status, platform_dispensary_id, created_at, updated_at FROM dispensaries `; const params = []; // Filter by menu_type if provided if (menu_type) { query += ` WHERE menu_type = $1`; params.push(menu_type); } query += ` ORDER BY name`; const result = await migrate_1.pool.query(query, params); res.json({ dispensaries: result.rows }); } catch (error) { console.error('Error fetching dispensaries:', error); res.status(500).json({ error: 'Failed to fetch dispensaries' }); } }); // Get menu type stats router.get('/stats/menu-types', async (req, res) => { try { const result = await migrate_1.pool.query(` SELECT menu_type, COUNT(*) as count FROM dispensaries GROUP BY menu_type ORDER BY count DESC `); res.json({ menu_types: result.rows, valid_types: VALID_MENU_TYPES }); } catch (error) { console.error('Error fetching menu type stats:', error); res.status(500).json({ error: 'Failed to fetch menu type stats' }); } }); // Get single dispensary by slug router.get('/:slug', async (req, res) => { try { const { slug } = req.params; const result = await migrate_1.pool.query(` SELECT id, azdhs_id, name, company_name, slug, address, city, state, zip, phone, email, website, dba_name, google_rating, google_review_count, status_line, azdhs_url, latitude, longitude, menu_url, menu_type, menu_provider, menu_provider_confidence, scraper_template, scraper_config, last_menu_scrape, menu_scrape_status, platform_dispensary_id, created_at, updated_at FROM dispensaries WHERE slug = $1 `, [slug]); if (result.rows.length === 0) { return res.status(404).json({ error: 'Dispensary not found' }); } res.json(result.rows[0]); } catch (error) { console.error('Error fetching dispensary:', error); res.status(500).json({ error: 'Failed to fetch dispensary' }); } }); // Update dispensary router.put('/:id', async (req, res) => { try { const { id } = req.params; const { dba_name, website, phone, email, google_rating, google_review_count, menu_url, menu_type, scraper_template, scraper_config, menu_scrape_status } = req.body; // Validate menu_type if provided if (menu_type !== undefined && menu_type !== null && menu_type !== '' && !VALID_MENU_TYPES.includes(menu_type)) { return res.status(400).json({ error: `Invalid menu_type. Must be one of: ${VALID_MENU_TYPES.join(', ')}` }); } const result = await migrate_1.pool.query(` UPDATE dispensaries SET dba_name = COALESCE($1, dba_name), website = COALESCE($2, website), phone = COALESCE($3, phone), email = COALESCE($4, email), google_rating = COALESCE($5, google_rating), google_review_count = COALESCE($6, google_review_count), menu_url = COALESCE($7, menu_url), menu_type = COALESCE($8, menu_type), scraper_template = COALESCE($9, scraper_template), scraper_config = COALESCE($10, scraper_config), menu_scrape_status = COALESCE($11, menu_scrape_status), updated_at = CURRENT_TIMESTAMP WHERE id = $12 RETURNING * `, [ dba_name, website, phone, email, google_rating, google_review_count, menu_url, menu_type, scraper_template, scraper_config, menu_scrape_status, id ]); if (result.rows.length === 0) { return res.status(404).json({ error: 'Dispensary not found' }); } res.json(result.rows[0]); } catch (error) { console.error('Error updating dispensary:', error); res.status(500).json({ error: 'Failed to update dispensary' }); } }); // Get products for a dispensary by slug router.get('/:slug/products', async (req, res) => { try { const { slug } = req.params; const { category } = req.query; // First get the dispensary ID from slug const dispensaryResult = await migrate_1.pool.query(` SELECT id FROM dispensaries WHERE slug = $1 `, [slug]); if (dispensaryResult.rows.length === 0) { return res.status(404).json({ error: 'Dispensary not found' }); } const dispensaryId = dispensaryResult.rows[0].id; // Build query for products let query = ` SELECT p.id, p.name, p.brand, p.variant, p.slug, p.description, p.regular_price, p.sale_price, p.thc_percentage, p.cbd_percentage, p.strain_type, p.terpenes, p.effects, p.flavors, p.image_url, p.dutchie_url, p.in_stock, p.created_at, p.updated_at FROM products p WHERE p.dispensary_id = $1 `; const params = [dispensaryId]; if (category) { query += ` AND p.category = $2`; params.push(category); } query += ` ORDER BY p.created_at DESC`; const result = await migrate_1.pool.query(query, params); res.json({ products: result.rows }); } catch (error) { console.error('Error fetching dispensary products:', error); res.status(500).json({ error: 'Failed to fetch products' }); } }); // Get unique brands for a dispensary by slug router.get('/:slug/brands', async (req, res) => { try { const { slug } = req.params; const { search } = req.query; // First get the dispensary ID from slug const dispensaryResult = await migrate_1.pool.query(` SELECT id FROM dispensaries WHERE slug = $1 `, [slug]); if (dispensaryResult.rows.length === 0) { return res.status(404).json({ error: 'Dispensary not found' }); } const dispensaryId = dispensaryResult.rows[0].id; // Build query with optional search filter let query = ` SELECT DISTINCT brand, COUNT(*) as product_count FROM products WHERE dispensary_id = $1 AND brand IS NOT NULL `; const params = [dispensaryId]; // Add search filter if provided if (search) { query += ` AND brand ILIKE $2`; params.push(`%${search}%`); } query += ` GROUP BY brand ORDER BY product_count DESC, brand ASC`; const result = await migrate_1.pool.query(query, params); res.json({ brands: result.rows }); } catch (error) { console.error('Error fetching dispensary brands:', error); res.status(500).json({ error: 'Failed to fetch brands' }); } }); // Get products with discounts/specials for a dispensary by slug router.get('/:slug/specials', async (req, res) => { try { const { slug } = req.params; const { search } = req.query; // First get the dispensary ID from slug const dispensaryResult = await migrate_1.pool.query(` SELECT id FROM dispensaries WHERE slug = $1 `, [slug]); if (dispensaryResult.rows.length === 0) { return res.status(404).json({ error: 'Dispensary not found' }); } const dispensaryId = dispensaryResult.rows[0].id; // Build query to get products with discounts let query = ` SELECT p.id, p.name, p.brand, p.variant, p.slug, p.description, p.regular_price, p.sale_price, p.discount_type, p.discount_value, p.thc_percentage, p.cbd_percentage, p.strain_type, p.terpenes, p.effects, p.flavors, p.image_url, p.dutchie_url, p.in_stock, p.created_at, p.updated_at FROM products p WHERE p.dispensary_id = $1 AND p.discount_type IS NOT NULL AND p.discount_value IS NOT NULL `; const params = [dispensaryId]; // Add search filter if provided if (search) { query += ` AND (p.name ILIKE $2 OR p.brand ILIKE $2 OR p.description ILIKE $2)`; params.push(`%${search}%`); } query += ` ORDER BY p.created_at DESC`; const result = await migrate_1.pool.query(query, params); res.json({ specials: result.rows }); } catch (error) { console.error('Error fetching dispensary specials:', error); res.status(500).json({ error: 'Failed to fetch specials' }); } }); // Trigger scraping for a dispensary router.post('/:slug/scrape', async (req, res) => { try { const { slug } = req.params; const { type } = req.body; // 'products' | 'brands' | 'specials' | 'all' if (!['products', 'brands', 'specials', 'all'].includes(type)) { return res.status(400).json({ error: 'Invalid type. Must be: products, brands, specials, or all' }); } // Get the dispensary const dispensaryResult = await migrate_1.pool.query(` SELECT id, name, slug, website, menu_url, scraper_template, scraper_config FROM dispensaries WHERE slug = $1 `, [slug]); if (dispensaryResult.rows.length === 0) { return res.status(404).json({ error: 'Dispensary not found' }); } const dispensary = dispensaryResult.rows[0]; if (!dispensary.menu_url && !dispensary.website) { return res.status(400).json({ error: 'Dispensary has no menu URL or website configured' }); } // Update last_menu_scrape time and status await migrate_1.pool.query(` UPDATE dispensaries SET last_menu_scrape = CURRENT_TIMESTAMP, menu_scrape_status = 'pending', updated_at = CURRENT_TIMESTAMP WHERE id = $1 `, [dispensary.id]); // Log the scrape request console.log(`[SCRAPE REQUEST] Dispensary: ${dispensary.name} (${slug}), Type: ${type}`); console.log(` Menu URL: ${dispensary.menu_url || dispensary.website}`); console.log(` Template: ${dispensary.scraper_template || 'N/A'}`); // TODO: Actually trigger the scraper here // For now, this is a placeholder that updates the status // You can integrate with your existing scraper infrastructure res.json({ success: true, message: `Scraping queued for ${dispensary.name}`, type, dispensary: { id: dispensary.id, name: dispensary.name, slug: dispensary.slug } }); } catch (error) { console.error('Error triggering scrape:', error); res.status(500).json({ error: 'Failed to trigger scraping' }); } }); // Update menu_type for a dispensary (dedicated endpoint) router.patch('/:id/menu-type', async (req, res) => { try { const { id } = req.params; const { menu_type } = req.body; // Validate menu_type if (menu_type !== null && menu_type !== '' && !VALID_MENU_TYPES.includes(menu_type)) { return res.status(400).json({ error: `Invalid menu_type. Must be one of: ${VALID_MENU_TYPES.join(', ')} (or null to clear)` }); } const result = await migrate_1.pool.query(` UPDATE dispensaries SET menu_type = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2 RETURNING id, name, slug, menu_type, menu_provider, menu_url `, [menu_type || null, id]); if (result.rows.length === 0) { return res.status(404).json({ error: 'Dispensary not found' }); } res.json({ success: true, dispensary: result.rows[0] }); } catch (error) { console.error('Error updating menu_type:', error); res.status(500).json({ error: 'Failed to update menu_type' }); } }); // Bulk update menu_type for multiple dispensaries router.post('/bulk/menu-type', async (req, res) => { try { const { dispensary_ids, menu_type } = req.body; if (!Array.isArray(dispensary_ids) || dispensary_ids.length === 0) { return res.status(400).json({ error: 'dispensary_ids must be a non-empty array' }); } // Validate menu_type if (menu_type !== null && menu_type !== '' && !VALID_MENU_TYPES.includes(menu_type)) { return res.status(400).json({ error: `Invalid menu_type. Must be one of: ${VALID_MENU_TYPES.join(', ')} (or null to clear)` }); } const result = await migrate_1.pool.query(` UPDATE dispensaries SET menu_type = $1, updated_at = CURRENT_TIMESTAMP WHERE id = ANY($2::int[]) RETURNING id, name, slug, menu_type `, [menu_type || null, dispensary_ids]); res.json({ success: true, updated_count: result.rowCount, dispensaries: result.rows }); } catch (error) { console.error('Error bulk updating menu_type:', error); res.status(500).json({ error: 'Failed to bulk update menu_type' }); } }); exports.default = router;