feat: AZ dispensary harmonization with Dutchie source of truth

Major changes:
- Add harmonize-az-dispensaries.ts script to sync dispensaries with Dutchie API
- Add migration 057 for crawl_enabled and dutchie_verified fields
- Remove legacy dutchie-az module (replaced by platforms/dutchie)
- Clean up deprecated crawlers, scrapers, and orchestrator code
- Update location-discovery to not fallback to slug when ID is missing
- Add crawl-rotator service for proxy rotation
- Add types/index.ts for shared type definitions
- Add woodpecker-agent k8s manifest

Harmonization script:
- Queries ConsumerDispensaries API for all 32 AZ cities
- Matches dispensaries by platform_dispensary_id (not slug)
- Updates existing records with full Dutchie data
- Creates new records for unmatched Dutchie dispensaries
- Disables dispensaries not found in Dutchie

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kelly
2025-12-08 10:19:49 -07:00
parent 948a732dd5
commit b7cfec0770
112 changed files with 3163 additions and 34694 deletions

View File

@@ -14,7 +14,7 @@
*/
import { Router, Request, Response } from 'express';
import { getPool, healthCheck as dbHealthCheck } from '../dutchie-az/db/connection';
import { pool } from '../db/pool';
import { getRedis } from '../lib/redis';
import * as fs from 'fs';
import * as path from 'path';
@@ -119,7 +119,7 @@ async function getApiHealth(): Promise<ApiHealth> {
async function getDbHealth(): Promise<DbHealth> {
const start = Date.now();
try {
const pool = getPool();
// pool imported from db/pool
await pool.query('SELECT 1');
return {
status: 'ok',
@@ -175,7 +175,7 @@ async function getRedisHealth(): Promise<RedisHealth> {
async function getWorkersHealth(): Promise<WorkersHealth> {
try {
const pool = getPool();
// pool imported from db/pool
// Get queue stats from v_queue_stats view or equivalent
const queueStatsResult = await pool.query(`
@@ -248,7 +248,7 @@ async function getWorkersHealth(): Promise<WorkersHealth> {
async function getCrawlsHealth(): Promise<CrawlsHealth> {
try {
const pool = getPool();
// pool imported from db/pool
// Get crawl statistics
const statsResult = await pool.query(`
@@ -299,7 +299,7 @@ async function getCrawlsHealth(): Promise<CrawlsHealth> {
async function getAnalyticsHealth(): Promise<AnalyticsHealth> {
try {
const pool = getPool();
// pool imported from db/pool
// Check analytics/aggregate job runs
const statsResult = await pool.query(`