Intial Version

This commit is contained in:
2025-12-03 18:00:10 -05:00
parent 43c4227da7
commit 0b86c88eb4
55 changed files with 8938 additions and 0 deletions

91
backend/src/index.ts Normal file
View File

@@ -0,0 +1,91 @@
import 'dotenv/config';
import server from './api/server';
import { ScraperScheduler } from './scrapers/scheduler';
import { PriceRepository } from './database/repository';
import { broadcastPriceUpdate } from './api/routes/websocket';
const PORT = parseInt(process.env.PORT || '3000', 10);
const HOST = process.env.HOST || '0.0.0.0';
const SCRAPE_INTERVAL = parseInt(process.env.SCRAPE_INTERVAL_MINUTES || '5', 10);
const repository = new PriceRepository();
const scheduler = new ScraperScheduler(SCRAPE_INTERVAL);
// Handle scrape results
scheduler.onScrapeComplete(async (results) => {
const startTime = Date.now();
try {
// Save all listings
const allListings = results.flatMap(r => r.listings);
if (allListings.length > 0) {
await repository.saveListings(allListings);
// Find and save lowest price
const lowestListing = allListings.reduce((min, listing) =>
listing.pricePerMillion < min.pricePerMillion ? listing : min
);
await repository.savePriceIndex(
lowestListing.pricePerMillion,
lowestListing.vendor,
lowestListing.seller
);
// Broadcast update to WebSocket clients
const latestPrices = await repository.getLatestPrices();
broadcastPriceUpdate({
timestamp: new Date(),
lowestPrice: lowestListing.pricePerMillion,
platform: lowestListing.vendor,
sellerName: lowestListing.seller,
allPrices: latestPrices.map((p: any) => ({
id: p.id,
platform: p.vendor,
sellerName: p.sellerName,
pricePerMillion: Number(p.usdPerMillion),
timestamp: p.timestamp,
url: p.url
}))
});
console.log(`✓ Broadcasted price update to ${latestPrices.length} listings`);
}
// Log successful scrape
const runtimeMs = Date.now() - startTime;
await repository.logScrape('success', `Saved ${allListings.length} listings`, runtimeMs);
console.log(`✓ Saved ${allListings.length} listings to database`);
} catch (error) {
const runtimeMs = Date.now() - startTime;
const message = error instanceof Error ? error.message : 'Unknown error';
await repository.logScrape('failure', message, runtimeMs);
console.error('✗ Failed to save listings:', message);
}
});
// Start server
const start = async () => {
try {
await server.listen({ port: PORT, host: HOST });
console.log(`Server listening on ${HOST}:${PORT}`);
// Start scraper
scheduler.start();
console.log(`Scraper started (interval: ${SCRAPE_INTERVAL} minutes)`);
} catch (err) {
server.log.error(err);
process.exit(1);
}
};
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('\nShutting down gracefully...');
await scheduler.close();
await server.close();
process.exit(0);
});
start();