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:
@@ -35,11 +35,11 @@ router.get('/overview', async (req, res) => {
|
||||
|
||||
// Top products
|
||||
const topProductsResult = await pool.query(`
|
||||
SELECT p.id, p.name, p.price, COUNT(c.id) as click_count
|
||||
SELECT p.id, p.name_raw as name, p.price_rec as price, COUNT(c.id) as click_count
|
||||
FROM clicks c
|
||||
JOIN products p ON c.product_id = p.id
|
||||
JOIN store_products p ON c.product_id = p.id
|
||||
WHERE c.clicked_at >= NOW() - INTERVAL '${parseInt(days as string)} days'
|
||||
GROUP BY p.id, p.name, p.price
|
||||
GROUP BY p.id, p.name_raw, p.price_rec
|
||||
ORDER BY click_count DESC
|
||||
LIMIT 10
|
||||
`);
|
||||
@@ -109,12 +109,12 @@ router.get('/campaigns/:id', async (req, res) => {
|
||||
|
||||
// Clicks by product in this campaign
|
||||
const byProductResult = await pool.query(`
|
||||
SELECT p.id, p.name, COUNT(c.id) as clicks
|
||||
SELECT p.id, p.name_raw as name, COUNT(c.id) as clicks
|
||||
FROM clicks c
|
||||
JOIN products p ON c.product_id = p.id
|
||||
JOIN store_products p ON c.product_id = p.id
|
||||
WHERE c.campaign_id = $1
|
||||
AND c.clicked_at >= NOW() - INTERVAL '${parseInt(days as string)} days'
|
||||
GROUP BY p.id, p.name
|
||||
GROUP BY p.id, p.name_raw
|
||||
ORDER BY clicks DESC
|
||||
`, [id]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user