Files
cannaiq/backend/dist/routes/dashboard.js
Kelly 66e07b2009 fix(monitor): remove non-existent worker columns from job_run_logs query
The job_run_logs table tracks scheduled job orchestration, not individual
worker jobs. Worker info (worker_id, worker_hostname) belongs on
dispensary_crawl_jobs, not job_run_logs.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 18:45:05 -07:00

117 lines
4.7 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const middleware_1 = require("../auth/middleware");
const connection_1 = require("../dutchie-az/db/connection");
const router = (0, express_1.Router)();
router.use(middleware_1.authMiddleware);
// Get dashboard stats - uses consolidated dutchie-az DB
router.get('/stats', async (req, res) => {
try {
// Store stats from dispensaries table in consolidated DB
const dispensariesResult = await (0, connection_1.query)(`
SELECT
COUNT(*) as total,
COUNT(*) FILTER (WHERE menu_type IS NOT NULL AND menu_type != 'unknown') as active,
COUNT(*) FILTER (WHERE platform_dispensary_id IS NOT NULL) as with_platform_id,
COUNT(*) FILTER (WHERE menu_url IS NOT NULL) as with_menu_url,
MIN(last_crawled_at) as oldest_crawl,
MAX(last_crawled_at) as latest_crawl
FROM dispensaries
`);
// Product stats from dutchie_products table
const productsResult = await (0, connection_1.query)(`
SELECT
COUNT(*) as total,
COUNT(*) FILTER (WHERE stock_status = 'in_stock') as in_stock,
COUNT(*) FILTER (WHERE primary_image_url IS NOT NULL) as with_images,
COUNT(DISTINCT brand_name) FILTER (WHERE brand_name IS NOT NULL AND brand_name != '') as unique_brands,
COUNT(DISTINCT dispensary_id) as dispensaries_with_products
FROM dutchie_products
`);
// Brand stats from dutchie_products
const brandResult = await (0, connection_1.query)(`
SELECT COUNT(DISTINCT brand_name) as total
FROM dutchie_products
WHERE brand_name IS NOT NULL AND brand_name != ''
`);
// Recent products added (last 24 hours)
const recentProductsResult = await (0, connection_1.query)(`
SELECT COUNT(*) as new_products_24h
FROM dutchie_products
WHERE created_at >= NOW() - INTERVAL '24 hours'
`);
// Combine results
const storeStats = dispensariesResult.rows[0];
const productStats = productsResult.rows[0];
res.json({
stores: {
total: parseInt(storeStats.total) || 0,
active: parseInt(storeStats.active) || 0,
with_menu_url: parseInt(storeStats.with_menu_url) || 0,
with_platform_id: parseInt(storeStats.with_platform_id) || 0,
oldest_crawl: storeStats.oldest_crawl,
latest_crawl: storeStats.latest_crawl
},
products: {
total: parseInt(productStats.total) || 0,
in_stock: parseInt(productStats.in_stock) || 0,
with_images: parseInt(productStats.with_images) || 0,
unique_brands: parseInt(productStats.unique_brands) || 0,
dispensaries_with_products: parseInt(productStats.dispensaries_with_products) || 0
},
brands: {
total: parseInt(brandResult.rows[0].total) || 0
},
campaigns: { total: 0, active: 0 }, // Legacy - no longer used
clicks: { clicks_24h: 0 }, // Legacy - no longer used
recent: recentProductsResult.rows[0]
});
}
catch (error) {
console.error('Error fetching dashboard stats:', error);
res.status(500).json({ error: 'Failed to fetch dashboard stats' });
}
});
// Get recent activity - from consolidated dutchie-az DB
router.get('/activity', async (req, res) => {
try {
const { limit = 20 } = req.query;
// Recent crawls from dispensaries (with product counts from dutchie_products)
const scrapesResult = await (0, connection_1.query)(`
SELECT
d.name,
d.last_crawled_at as last_scraped_at,
d.product_count
FROM dispensaries d
WHERE d.last_crawled_at IS NOT NULL
ORDER BY d.last_crawled_at DESC
LIMIT $1
`, [limit]);
// Recent products from dutchie_products
const productsResult = await (0, connection_1.query)(`
SELECT
p.name,
0 as price,
p.brand_name as brand,
p.thc as thc_percentage,
p.cbd as cbd_percentage,
d.name as store_name,
p.created_at as first_seen_at
FROM dutchie_products p
JOIN dispensaries d ON p.dispensary_id = d.id
ORDER BY p.created_at DESC
LIMIT $1
`, [limit]);
res.json({
recent_scrapes: scrapesResult.rows,
recent_products: productsResult.rows
});
}
catch (error) {
console.error('Error fetching dashboard activity:', error);
res.status(500).json({ error: 'Failed to fetch dashboard activity' });
}
});
exports.default = router;