feat: Add v2 architecture with multi-state support and orchestrator services
Major additions: - Multi-state expansion: states table, StateSelector, NationalDashboard, StateHeatmap, CrossStateCompare - Orchestrator services: trace service, error taxonomy, retry manager, proxy rotator - Discovery system: dutchie discovery service, geo validation, city seeding scripts - Analytics infrastructure: analytics v2 routes, brand/pricing/stores intelligence pages - Local development: setup-local.sh starts all 5 services (postgres, backend, cannaiq, findadispo, findagram) - Migrations 037-056: crawler profiles, states, analytics indexes, worker metadata Frontend pages added: - Discovery, ChainsDashboard, IntelligenceBrands, IntelligencePricing, IntelligenceStores - StateHeatmap, CrossStateCompare, SyncInfoPanel Components added: - StateSelector, OrchestratorTraceModal, WorkflowStepper 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
118
backend/src/crawlers/dutchie/stores/trulieve-scottsdale.ts
Normal file
118
backend/src/crawlers/dutchie/stores/trulieve-scottsdale.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Trulieve Scottsdale - Per-Store Dutchie Crawler
|
||||
*
|
||||
* Store ID: 101
|
||||
* Profile Key: trulieve-scottsdale
|
||||
* Platform Dispensary ID: 5eaf489fa8a61801212577cc
|
||||
*
|
||||
* Phase 1: Identity implementation - no overrides, just uses base Dutchie logic.
|
||||
* Future: Add store-specific selectors, timing, or custom logic as needed.
|
||||
*/
|
||||
|
||||
import {
|
||||
BaseDutchieCrawler,
|
||||
StoreCrawlOptions,
|
||||
CrawlResult,
|
||||
DutchieSelectors,
|
||||
crawlProducts as baseCrawlProducts,
|
||||
} from '../../base/base-dutchie';
|
||||
import { Dispensary } from '../../../dutchie-az/types';
|
||||
|
||||
// Re-export CrawlResult for the orchestrator
|
||||
export { CrawlResult };
|
||||
|
||||
// ============================================================
|
||||
// STORE CONFIGURATION
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Store-specific configuration
|
||||
* These can be used to customize crawler behavior for this store
|
||||
*/
|
||||
export const STORE_CONFIG = {
|
||||
storeId: 101,
|
||||
profileKey: 'trulieve-scottsdale',
|
||||
name: 'Trulieve of Scottsdale Dispensary',
|
||||
platformDispensaryId: '5eaf489fa8a61801212577cc',
|
||||
|
||||
// Store-specific overrides (none for Phase 1)
|
||||
customOptions: {
|
||||
// Example future overrides:
|
||||
// pricingType: 'rec',
|
||||
// useBothModes: true,
|
||||
// customHeaders: {},
|
||||
// maxRetries: 3,
|
||||
},
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// STORE CRAWLER CLASS
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* TrulieveScottsdaleCrawler - Per-store crawler for Trulieve Scottsdale
|
||||
*
|
||||
* Phase 1: Identity implementation - extends BaseDutchieCrawler with no overrides.
|
||||
* Future phases can override methods like:
|
||||
* - getCName() for custom slug handling
|
||||
* - crawlProducts() for completely custom logic
|
||||
* - Add hooks for pre/post processing
|
||||
*/
|
||||
export class TrulieveScottsdaleCrawler extends BaseDutchieCrawler {
|
||||
constructor(dispensary: Dispensary, options: StoreCrawlOptions = {}) {
|
||||
// Merge store-specific options with provided options
|
||||
const mergedOptions: StoreCrawlOptions = {
|
||||
...STORE_CONFIG.customOptions,
|
||||
...options,
|
||||
};
|
||||
|
||||
super(dispensary, mergedOptions);
|
||||
}
|
||||
|
||||
// Phase 1: No overrides - use base implementation
|
||||
// Future phases can add overrides here:
|
||||
//
|
||||
// async crawlProducts(): Promise<CrawlResult> {
|
||||
// // Custom pre-processing
|
||||
// // ...
|
||||
// const result = await super.crawlProducts();
|
||||
// // Custom post-processing
|
||||
// // ...
|
||||
// return result;
|
||||
// }
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// EXPORTED CRAWL FUNCTION
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Main entry point for the orchestrator
|
||||
*
|
||||
* The orchestrator calls: mod.crawlProducts(dispensary, options)
|
||||
* This function creates a TrulieveScottsdaleCrawler and runs it.
|
||||
*/
|
||||
export async function crawlProducts(
|
||||
dispensary: Dispensary,
|
||||
options: StoreCrawlOptions = {}
|
||||
): Promise<CrawlResult> {
|
||||
console.log(`[TrulieveScottsdale] Using per-store crawler for ${dispensary.name}`);
|
||||
|
||||
const crawler = new TrulieveScottsdaleCrawler(dispensary, options);
|
||||
return crawler.crawlProducts();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// FACTORY FUNCTION (alternative API)
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Create a crawler instance without running it
|
||||
* Useful for testing or when you need to configure before running
|
||||
*/
|
||||
export function createCrawler(
|
||||
dispensary: Dispensary,
|
||||
options: StoreCrawlOptions = {}
|
||||
): TrulieveScottsdaleCrawler {
|
||||
return new TrulieveScottsdaleCrawler(dispensary, options);
|
||||
}
|
||||
Reference in New Issue
Block a user