feat: SEO template library, discovery pipeline, and orchestrator enhancements
## SEO Template Library - Add complete template library with 7 page types (state, city, category, brand, product, search, regeneration) - Add Template Library tab in SEO Orchestrator with accordion-based editors - Add template preview, validation, and variable injection engine - Add API endpoints: /api/seo/templates, preview, validate, generate, regenerate ## Discovery Pipeline - Add promotion.ts for discovery location validation and promotion - Add discover-all-states.ts script for multi-state discovery - Add promotion log migration (067) - Enhance discovery routes and types ## Orchestrator & Admin - Add crawl_enabled filter to stores page - Add API permissions page - Add job queue management - Add price analytics routes - Add markets and intelligence routes - Enhance dashboard and worker monitoring ## Infrastructure - Add migrations for worker definitions, SEO settings, field alignment - Add canonical pipeline for scraper v2 - Update hydration and sync orchestrator - Enhance multi-state query service 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -234,99 +234,94 @@ export async function syncProductsToCanonical(
|
||||
|
||||
const result = await pool.query(
|
||||
`INSERT INTO store_products (
|
||||
dispensary_id, state_id, provider, provider_product_id,
|
||||
provider_brand_id, provider_dispensary_id, enterprise_product_id,
|
||||
legacy_dutchie_product_id,
|
||||
name, brand_name, category, subcategory, product_type, strain_type,
|
||||
description, effects, cannabinoids,
|
||||
thc_percent, cbd_percent, thc_content_text, cbd_content_text,
|
||||
is_in_stock, stock_status, stock_quantity,
|
||||
total_quantity_available, total_kiosk_quantity_available,
|
||||
image_url, local_image_url, local_image_thumb_url, local_image_medium_url,
|
||||
original_image_url, additional_images,
|
||||
is_on_special, is_featured, medical_only, rec_only,
|
||||
dispensary_id, provider, provider_product_id, provider_brand_id,
|
||||
platform_dispensary_id, external_product_id,
|
||||
name_raw, brand_name_raw, category_raw, subcategory_raw, strain_type,
|
||||
description, effects, cannabinoids_v2,
|
||||
thc_percent, cbd_percent, thc_content, cbd_content,
|
||||
is_in_stock, stock_status, stock_quantity, total_quantity_available,
|
||||
image_url, primary_image_url, images,
|
||||
is_on_special, featured, medical_only, rec_only,
|
||||
is_below_threshold, is_below_kiosk_threshold,
|
||||
platform_status, c_name, weight, options, measurements,
|
||||
first_seen_at, last_seen_at, updated_at
|
||||
status, c_name, weight, measurements,
|
||||
first_seen_at, last_seen_at, created_at, updated_at
|
||||
) VALUES (
|
||||
$1, $2, 'dutchie', $3,
|
||||
$4, $5, $6,
|
||||
$7,
|
||||
$8, $9, $10, $11, $12, $13,
|
||||
$14, $15, $16,
|
||||
$17, $18, $19, $20,
|
||||
$21, $22, $23,
|
||||
$24, $25,
|
||||
$26, $27, $28, $29,
|
||||
$30, $31,
|
||||
$32, $33, $34, $35,
|
||||
$36, $37,
|
||||
$38, $39, $40, $41, $42,
|
||||
$43, $44, NOW()
|
||||
$1, 'dutchie', $2, $3,
|
||||
$4, $5,
|
||||
$6, $7, $8, $9, $10,
|
||||
$11, $12, $13,
|
||||
$14, $15, $16, $17,
|
||||
$18, $19, $20, $21,
|
||||
$22, $23, $24,
|
||||
$25, $26, $27, $28,
|
||||
$29, $30,
|
||||
$31, $32, $33, $34,
|
||||
$35, $36, NOW(), NOW()
|
||||
)
|
||||
ON CONFLICT (dispensary_id, provider, provider_product_id)
|
||||
DO UPDATE SET
|
||||
legacy_dutchie_product_id = EXCLUDED.legacy_dutchie_product_id,
|
||||
name = EXCLUDED.name,
|
||||
brand_name = EXCLUDED.brand_name,
|
||||
category = EXCLUDED.category,
|
||||
subcategory = EXCLUDED.subcategory,
|
||||
name_raw = EXCLUDED.name_raw,
|
||||
brand_name_raw = EXCLUDED.brand_name_raw,
|
||||
category_raw = EXCLUDED.category_raw,
|
||||
subcategory_raw = EXCLUDED.subcategory_raw,
|
||||
strain_type = EXCLUDED.strain_type,
|
||||
is_in_stock = EXCLUDED.is_in_stock,
|
||||
stock_status = EXCLUDED.stock_status,
|
||||
stock_quantity = EXCLUDED.stock_quantity,
|
||||
total_quantity_available = EXCLUDED.total_quantity_available,
|
||||
thc_percent = EXCLUDED.thc_percent,
|
||||
cbd_percent = EXCLUDED.cbd_percent,
|
||||
thc_content = EXCLUDED.thc_content,
|
||||
cbd_content = EXCLUDED.cbd_content,
|
||||
image_url = EXCLUDED.image_url,
|
||||
local_image_url = EXCLUDED.local_image_url,
|
||||
primary_image_url = EXCLUDED.primary_image_url,
|
||||
is_on_special = EXCLUDED.is_on_special,
|
||||
platform_status = EXCLUDED.platform_status,
|
||||
status = EXCLUDED.status,
|
||||
description = COALESCE(EXCLUDED.description, store_products.description),
|
||||
effects = COALESCE(EXCLUDED.effects, store_products.effects),
|
||||
cannabinoids_v2 = COALESCE(EXCLUDED.cannabinoids_v2, store_products.cannabinoids_v2),
|
||||
weight = EXCLUDED.weight,
|
||||
measurements = EXCLUDED.measurements,
|
||||
last_seen_at = NOW(),
|
||||
updated_at = NOW()
|
||||
RETURNING (xmax = 0) as is_new`,
|
||||
[
|
||||
dispensaryId,
|
||||
stateId,
|
||||
p.external_product_id,
|
||||
p.brand_id,
|
||||
p.platform_dispensary_id,
|
||||
p.enterprise_product_id,
|
||||
p.id,
|
||||
p.name,
|
||||
p.brand_name,
|
||||
p.category || p.type,
|
||||
p.subcategory,
|
||||
p.type,
|
||||
p.strain_type,
|
||||
p.description,
|
||||
p.effects,
|
||||
p.cannabinoids_v2,
|
||||
thcPercent,
|
||||
cbdPercent,
|
||||
p.thc_content,
|
||||
p.cbd_content,
|
||||
isInStock,
|
||||
stockStatus,
|
||||
p.total_quantity_available,
|
||||
p.total_quantity_available,
|
||||
p.total_kiosk_quantity_available,
|
||||
p.primary_image_url,
|
||||
p.local_image_url,
|
||||
p.local_image_thumb_url,
|
||||
p.local_image_medium_url,
|
||||
p.original_image_url,
|
||||
p.additional_images,
|
||||
p.special || false,
|
||||
p.featured || false,
|
||||
p.medical_only || false,
|
||||
p.rec_only || false,
|
||||
p.is_below_threshold || false,
|
||||
p.is_below_kiosk_threshold || false,
|
||||
p.status,
|
||||
p.c_name,
|
||||
p.weight,
|
||||
p.options,
|
||||
p.measurements,
|
||||
p.first_seen_at || p.updated_at,
|
||||
p.last_seen_at || p.updated_at,
|
||||
dispensaryId, // $1
|
||||
p.external_product_id, // $2
|
||||
p.brand_id, // $3
|
||||
p.platform_dispensary_id, // $4
|
||||
p.external_product_id, // $5 external_product_id
|
||||
p.name, // $6
|
||||
p.brand_name, // $7
|
||||
p.type || p.category, // $8 category_raw
|
||||
p.subcategory, // $9
|
||||
p.strain_type, // $10
|
||||
p.description, // $11
|
||||
p.effects, // $12
|
||||
p.cannabinoids_v2, // $13
|
||||
thcPercent, // $14
|
||||
cbdPercent, // $15
|
||||
p.thc_content, // $16
|
||||
p.cbd_content, // $17
|
||||
isInStock, // $18
|
||||
stockStatus, // $19
|
||||
p.total_quantity_available || 0, // $20 stock_quantity
|
||||
p.total_quantity_available || 0, // $21
|
||||
p.primary_image_url, // $22 image_url
|
||||
p.primary_image_url, // $23
|
||||
p.additional_images, // $24 images
|
||||
p.special || false, // $25
|
||||
p.featured || false, // $26
|
||||
p.medical_only || false, // $27
|
||||
p.rec_only || false, // $28
|
||||
p.is_below_threshold || false, // $29
|
||||
p.is_below_kiosk_threshold || false, // $30
|
||||
p.status, // $31
|
||||
p.c_name, // $32
|
||||
p.weight, // $33
|
||||
p.measurements, // $34
|
||||
p.first_seen_at || p.updated_at, // $35
|
||||
p.last_seen_at || p.updated_at, // $36
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user