feat: Add stale process monitor, users route, landing page, archive old scripts

- Add backend stale process monitoring API (/api/stale-processes)
- Add users management route
- Add frontend landing page and stale process monitor UI on /scraper-tools
- Move old development scripts to backend/archive/
- Update frontend build with new features

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Kelly
2025-12-05 04:07:31 -07:00
parent d2d44d2aeb
commit d91c55a344
3115 changed files with 5755 additions and 719 deletions

View File

@@ -0,0 +1,65 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const pg_1 = require("pg");
const pool = new pg_1.Pool({ connectionString: process.env.DATABASE_URL });
/**
* Creates `stores` table records for all dispensaries that:
* 1. Have menu_type = 'dutchie' AND platform_dispensary_id (ready for GraphQL crawl)
* 2. Don't already have a linked stores record
*
* The stores table is required by the scraper engine (scrapeStore function)
*/
async function bootstrapStores() {
console.log('=== Bootstrapping stores for Dutchie dispensaries ===\n');
// Find all dutchie dispensaries without linked stores
const result = await pool.query(`
SELECT d.id, d.name, d.slug, d.menu_type, d.platform_dispensary_id, d.menu_url
FROM dispensaries d
LEFT JOIN stores s ON s.dispensary_id = d.id
WHERE d.menu_type = 'dutchie'
AND d.platform_dispensary_id IS NOT NULL
AND s.id IS NULL
ORDER BY d.id
`);
console.log(`Found ${result.rows.length} dispensaries needing store records\n`);
let created = 0;
let errors = 0;
for (const d of result.rows) {
try {
// Insert store record linking to dispensary
// Note: stores table only has basic fields: name, slug, dispensary_id, dutchie_url
// The platform_dispensary_id for GraphQL crawling lives in the dispensaries table
const insertResult = await pool.query(`
INSERT INTO stores (
name,
slug,
dispensary_id,
active,
scrape_enabled,
created_at,
updated_at
) VALUES ($1, $2, $3, true, true, NOW(), NOW())
RETURNING id
`, [
d.name,
d.slug || d.name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
d.id
]);
console.log(`[CREATED] Store ${insertResult.rows[0].id} for dispensary ${d.id}: ${d.name}`);
created++;
}
catch (e) {
console.error(`[ERROR] Dispensary ${d.id} (${d.name}): ${e.message}`);
errors++;
}
}
console.log('\n=== Bootstrap Summary ===');
console.log(`Created: ${created}`);
console.log(`Errors: ${errors}`);
console.log(`Total needing stores: ${result.rows.length}`);
await pool.end();
}
bootstrapStores().catch(e => {
console.error('Fatal error:', e.message);
process.exit(1);
});