Files
net/scripts/geo_manager.js
cawcenter fd9f428dcd Phase 1: Foundation & Stability Infrastructure
 BullMQ job queue system installed and configured
 Zod validation schemas for all collections
 Spintax validator with integrity checks
 Work log helper for centralized logging
 Transaction wrapper for safe database operations
 Batch operation utilities with rate limiting
 Circuit breaker for WordPress/Directus resilience
 Dry-run mode for preview generation
 Version management system
 Environment configuration

This establishes the bulletproof infrastructure for Spark Alpha.
2025-12-13 12:12:17 -05:00

297 lines
10 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Geo Intelligence Manager
* Easily add, remove, and manage locations in geo_intelligence collection
*/
const DIRECTUS_URL = 'https://spark.jumpstartscaling.com';
const ADMIN_TOKEN = 'SufWLAbsqmbbqF_gg5I70ng8wE1zXt-a';
async function makeRequest(endpoint, method = 'GET', body = null) {
const options = {
method,
headers: {
'Authorization': `Bearer ${ADMIN_TOKEN}`,
'Content-Type': 'application/json'
}
};
if (body) {
options.body = JSON.stringify(body);
}
const response = await fetch(`${DIRECTUS_URL}${endpoint}`, options);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API Error: ${response.status} - ${errorText}`);
}
return response.json();
}
async function listLocations() {
console.log('\n📍 GEO INTELLIGENCE LOCATIONS\n');
console.log('═'.repeat(80));
const data = await makeRequest('/items/geo_intelligence?limit=-1');
const locations = data.data || [];
if (locations.length === 0) {
console.log(' No locations found.\n');
return;
}
console.log(`\n ID | City | State | Country | Cluster | Status`);
console.log('─'.repeat(80));
locations.forEach(loc => {
const id = (loc.id || 'N/A').toString().padEnd(4);
const city = (loc.city || 'N/A').padEnd(20);
const state = (loc.state || 'N/A').padEnd(6);
const country = (loc.country || 'US').padEnd(8);
const cluster = (loc.geo_cluster || 'none').padEnd(13);
const status = loc.is_active ? '✅ Active' : '❌ Inactive';
console.log(` ${id} ${city} ${state} ${country} ${cluster} ${status}`);
});
console.log('\n═'.repeat(80));
console.log(` Total: ${locations.length} locations\n`);
}
async function addLocation(city, state, country = 'US', cluster = null) {
console.log(`\n Adding location: ${city}, ${state}, ${country}...`);
const newLocation = {
city,
state,
country,
geo_cluster: cluster,
is_active: true,
population: null,
latitude: null,
longitude: null
};
try {
const result = await makeRequest('/items/geo_intelligence', 'POST', newLocation);
console.log(`✅ Location added successfully! ID: ${result.data.id}\n`);
return result.data;
} catch (err) {
console.log(`❌ Failed to add location: ${err.message}\n`);
return null;
}
}
async function addBulkLocations(locations) {
console.log(`\n Adding ${locations.length} locations in bulk...\n`);
let added = 0;
let failed = 0;
for (const loc of locations) {
try {
const newLocation = {
city: loc.city,
state: loc.state,
country: loc.country || 'US',
geo_cluster: loc.cluster || null,
is_active: true,
population: loc.population || null,
latitude: loc.latitude || null,
longitude: loc.longitude || null
};
await makeRequest('/items/geo_intelligence', 'POST', newLocation);
console.log(` ✅ Added: ${loc.city}, ${loc.state}`);
added++;
} catch (err) {
console.log(` ❌ Failed: ${loc.city}, ${loc.state} - ${err.message}`);
failed++;
}
}
console.log(`\n📊 Summary: ${added} added, ${failed} failed\n`);
}
async function removeLocation(id) {
console.log(`\n🗑️ Removing location ID: ${id}...`);
try {
await makeRequest(`/items/geo_intelligence/${id}`, 'DELETE');
console.log(`✅ Location removed successfully!\n`);
return true;
} catch (err) {
console.log(`❌ Failed to remove location: ${err.message}\n`);
return false;
}
}
async function toggleLocationStatus(id, isActive) {
console.log(`\n🔄 Setting location ID ${id} to ${isActive ? 'active' : 'inactive'}...`);
try {
await makeRequest(`/items/geo_intelligence/${id}`, 'PATCH', { is_active: isActive });
console.log(`✅ Location status updated!\n`);
return true;
} catch (err) {
console.log(`❌ Failed to update location: ${err.message}\n`);
return false;
}
}
async function updateLocation(id, updates) {
console.log(`\n✏️ Updating location ID: ${id}...`);
try {
const result = await makeRequest(`/items/geo_intelligence/${id}`, 'PATCH', updates);
console.log(`✅ Location updated successfully!\n`);
return result.data;
} catch (err) {
console.log(`❌ Failed to update location: ${err.message}\n`);
return null;
}
}
async function importFromCSV(csvPath) {
const fs = require('fs');
console.log(`\n📥 Importing locations from CSV: ${csvPath}...\n`);
try {
const csvContent = fs.readFileSync(csvPath, 'utf-8');
const lines = csvContent.split('\n').filter(l => l.trim());
const headers = lines[0].split(',').map(h => h.trim());
const locations = [];
for (let i = 1; i < lines.length; i++) {
const values = lines[i].split(',').map(v => v.trim());
const loc = {};
headers.forEach((header, index) => {
loc[header.toLowerCase()] = values[index];
});
locations.push(loc);
}
console.log(`📊 Found ${locations.length} locations in CSV\n`);
await addBulkLocations(locations);
} catch (err) {
console.log(`❌ CSV import failed: ${err.message}\n`);
}
}
// Sample data for quick setup
const SAMPLE_US_CITIES = [
{ city: 'New York', state: 'NY', cluster: 'northeast', population: 8336817 },
{ city: 'Los Angeles', state: 'CA', cluster: 'west', population: 3979576 },
{ city: 'Chicago', state: 'IL', cluster: 'midwest', population: 2693976 },
{ city: 'Houston', state: 'TX', cluster: 'south', population: 2320268 },
{ city: 'Phoenix', state: 'AZ', cluster: 'southwest', population: 1680992 },
{ city: 'Philadelphia', state: 'PA', cluster: 'northeast', population: 1584064 },
{ city: 'San Antonio', state: 'TX', cluster: 'south', population: 1547253 },
{ city: 'San Diego', state: 'CA', cluster: 'west', population: 1423851 },
{ city: 'Dallas', state: 'TX', cluster: 'south', population: 1343573 },
{ city: 'San Jose', state: 'CA', cluster: 'west', population: 1021795 },
{ city: 'Austin', state: 'TX', cluster: 'south', population: 978908 },
{ city: 'Jacksonville', state: 'FL', cluster: 'southeast', population: 911507 },
{ city: 'Fort Worth', state: 'TX', cluster: 'south', population: 909585 },
{ city: 'Columbus', state: 'OH', cluster: 'midwest', population: 898553 },
{ city: 'Charlotte', state: 'NC', cluster: 'southeast', population: 885708 },
{ city: 'San Francisco', state: 'CA', cluster: 'west', population: 881549 },
{ city: 'Indianapolis', state: 'IN', cluster: 'midwest', population: 876384 },
{ city: 'Seattle', state: 'WA', cluster: 'northwest', population: 753675 },
{ city: 'Denver', state: 'CO', cluster: 'mountain', population: 727211 },
{ city: 'Boston', state: 'MA', cluster: 'northeast', population: 692600 }
];
// CLI Interface
async function main() {
const args = process.argv.slice(2);
const command = args[0];
if (command === 'list') {
await listLocations();
} else if (command === 'add') {
const city = args[1];
const state = args[2];
const country = args[3] || 'US';
const cluster = args[4] || null;
if (city && state) {
await addLocation(city, state, country, cluster);
} else {
console.log('Usage: node geo_manager.js add <city> <state> [country] [cluster]');
}
} else if (command === 'remove') {
const id = args[1];
if (id) {
await removeLocation(id);
} else {
console.log('Usage: node geo_manager.js remove <id>');
}
} else if (command === 'activate' || command === 'deactivate') {
const id = args[1];
if (id) {
await toggleLocationStatus(id, command === 'activate');
} else {
console.log(`Usage: node geo_manager.js ${command} <id>`);
}
} else if (command === 'update') {
const id = args[1];
const field = args[2];
const value = args[3];
if (id && field && value) {
const updates = { [field]: value };
await updateLocation(id, updates);
} else {
console.log('Usage: node geo_manager.js update <id> <field> <value>');
}
} else if (command === 'import-csv') {
const csvPath = args[1];
if (csvPath) {
await importFromCSV(csvPath);
} else {
console.log('Usage: node geo_manager.js import-csv <path-to-csv>');
}
} else if (command === 'seed-us-cities') {
console.log('\n🌱 Seeding with top 20 US cities...\n');
await addBulkLocations(SAMPLE_US_CITIES);
} else {
console.log('\n📍 GEO INTELLIGENCE MANAGER\n');
console.log('Commands:');
console.log(' list List all locations');
console.log(' add <city> <state> [country] [cluster] Add a location');
console.log(' remove <id> Remove a location');
console.log(' activate <id> Activate a location');
console.log(' deactivate <id> Deactivate a location');
console.log(' update <id> <field> <value> Update a location field');
console.log(' import-csv <path> Import from CSV file');
console.log(' seed-us-cities Add top 20 US cities');
console.log('\nExamples:');
console.log(' node geo_manager.js list');
console.log(' node geo_manager.js add Miami FL US southeast');
console.log(' node geo_manager.js remove 123');
console.log(' node geo_manager.js seed-us-cities');
console.log(' node geo_manager.js import-csv locations.csv\n');
console.log('CSV Format:');
console.log(' city,state,country,cluster,population');
console.log(' Miami,FL,US,southeast,467963\n');
}
}
main().catch(err => {
console.error('❌ Error:', err.message);
process.exit(1);
});