← back to Designer Wallcoverings
DW-Agents/dw-agents/amazon-data-cache.ts
322 lines
/**
* Amazon Data Cache System
*
* Stores ALL Amazon data locally so it's available even when Amazon is down:
* - Product details
* - Price history
* - Order history
* - Product images and descriptions
* - Search results
*/
import * as fs from 'fs';
import * as path from 'path';
const CACHE_DIR = '/root/Projects/Designer-Wallcoverings/DW-Agents/data/amazon-cache';
const ORDERS_CACHE = path.join(CACHE_DIR, 'orders.json');
const PRODUCTS_CACHE = path.join(CACHE_DIR, 'products.json');
const PRICE_HISTORY_CACHE = path.join(CACHE_DIR, 'price-history.json');
const SEARCH_CACHE = path.join(CACHE_DIR, 'searches.json');
// Ensure cache directory exists
if (!fs.existsSync(CACHE_DIR)) {
fs.mkdirSync(CACHE_DIR, { recursive: true });
console.log('✅ Created Amazon cache directory');
}
export interface CachedOrder {
orderId: string;
timestamp: Date;
item: string;
price: number;
vendor: string;
status: string;
tracking?: string;
productUrl?: string;
productImage?: string;
asin?: string;
}
export interface CachedProduct {
asin: string;
title: string;
description: string;
currentPrice: number;
imageUrl: string;
productUrl: string;
availability: string;
rating?: number;
reviewCount?: number;
lastUpdated: Date;
category?: string;
brand?: string;
}
export interface PriceHistoryEntry {
asin: string;
productTitle: string;
timestamp: Date;
price: number;
vendor: string;
}
export interface SearchCacheEntry {
query: string;
timestamp: Date;
results: CachedProduct[];
}
/**
* Save order to cache
*/
export function cacheOrder(order: CachedOrder): void {
try {
let orders: CachedOrder[] = [];
if (fs.existsSync(ORDERS_CACHE)) {
const data = fs.readFileSync(ORDERS_CACHE, 'utf-8');
orders = JSON.parse(data);
}
// Check if order already exists
const existingIndex = orders.findIndex(o => o.orderId === order.orderId);
if (existingIndex >= 0) {
orders[existingIndex] = order;
console.log(`📦 Updated cached order: ${order.orderId}`);
} else {
orders.push(order);
console.log(`📦 Cached new order: ${order.orderId}`);
}
fs.writeFileSync(ORDERS_CACHE, JSON.stringify(orders, null, 2));
} catch (error) {
console.error('❌ Error caching order:', error);
}
}
/**
* Save product to cache
*/
export function cacheProduct(product: CachedProduct): void {
try {
let products: { [asin: string]: CachedProduct } = {};
if (fs.existsSync(PRODUCTS_CACHE)) {
const data = fs.readFileSync(PRODUCTS_CACHE, 'utf-8');
products = JSON.parse(data);
}
products[product.asin] = product;
fs.writeFileSync(PRODUCTS_CACHE, JSON.stringify(products, null, 2));
console.log(`📦 Cached product: ${product.asin} - ${product.title.substring(0, 50)}...`);
// Also save to price history
savePriceHistory({
asin: product.asin,
productTitle: product.title,
timestamp: new Date(),
price: product.currentPrice,
vendor: 'Amazon'
});
} catch (error) {
console.error('❌ Error caching product:', error);
}
}
/**
* Save price history entry
*/
export function savePriceHistory(entry: PriceHistoryEntry): void {
try {
let history: PriceHistoryEntry[] = [];
if (fs.existsSync(PRICE_HISTORY_CACHE)) {
const data = fs.readFileSync(PRICE_HISTORY_CACHE, 'utf-8');
history = JSON.parse(data);
}
history.push(entry);
// Keep last 10,000 entries
if (history.length > 10000) {
history = history.slice(-10000);
}
fs.writeFileSync(PRICE_HISTORY_CACHE, JSON.stringify(history, null, 2));
} catch (error) {
console.error('❌ Error saving price history:', error);
}
}
/**
* Cache search results
*/
export function cacheSearch(query: string, results: CachedProduct[]): void {
try {
let searches: SearchCacheEntry[] = [];
if (fs.existsSync(SEARCH_CACHE)) {
const data = fs.readFileSync(SEARCH_CACHE, 'utf-8');
searches = JSON.parse(data);
}
// Remove old entry for same query
searches = searches.filter(s => s.query.toLowerCase() !== query.toLowerCase());
searches.push({
query,
timestamp: new Date(),
results
});
// Keep last 100 searches
if (searches.length > 100) {
searches = searches.slice(-100);
}
fs.writeFileSync(SEARCH_CACHE, JSON.stringify(searches, null, 2));
console.log(`🔍 Cached search: "${query}" (${results.length} results)`);
} catch (error) {
console.error('❌ Error caching search:', error);
}
}
/**
* Get cached orders
*/
export function getCachedOrders(): CachedOrder[] {
try {
if (fs.existsSync(ORDERS_CACHE)) {
const data = fs.readFileSync(ORDERS_CACHE, 'utf-8');
return JSON.parse(data);
}
} catch (error) {
console.error('❌ Error reading cached orders:', error);
}
return [];
}
/**
* Get cached product by ASIN
*/
export function getCachedProduct(asin: string): CachedProduct | null {
try {
if (fs.existsSync(PRODUCTS_CACHE)) {
const data = fs.readFileSync(PRODUCTS_CACHE, 'utf-8');
const products = JSON.parse(data);
return products[asin] || null;
}
} catch (error) {
console.error('❌ Error reading cached product:', error);
}
return null;
}
/**
* Get all cached products
*/
export function getAllCachedProducts(): CachedProduct[] {
try {
if (fs.existsSync(PRODUCTS_CACHE)) {
const data = fs.readFileSync(PRODUCTS_CACHE, 'utf-8');
const products = JSON.parse(data);
return Object.values(products);
}
} catch (error) {
console.error('❌ Error reading cached products:', error);
}
return [];
}
/**
* Get price history for product
*/
export function getPriceHistory(asin: string): PriceHistoryEntry[] {
try {
if (fs.existsSync(PRICE_HISTORY_CACHE)) {
const data = fs.readFileSync(PRICE_HISTORY_CACHE, 'utf-8');
const history: PriceHistoryEntry[] = JSON.parse(data);
return history.filter(h => h.asin === asin);
}
} catch (error) {
console.error('❌ Error reading price history:', error);
}
return [];
}
/**
* Search cached products
*/
export function searchCachedProducts(query: string): CachedProduct[] {
try {
const allProducts = getAllCachedProducts();
const lowerQuery = query.toLowerCase();
return allProducts.filter(p =>
p.title.toLowerCase().includes(lowerQuery) ||
p.description?.toLowerCase().includes(lowerQuery) ||
p.brand?.toLowerCase().includes(lowerQuery) ||
p.category?.toLowerCase().includes(lowerQuery)
);
} catch (error) {
console.error('❌ Error searching cached products:', error);
}
return [];
}
/**
* Get cached search
*/
export function getCachedSearch(query: string): SearchCacheEntry | null {
try {
if (fs.existsSync(SEARCH_CACHE)) {
const data = fs.readFileSync(SEARCH_CACHE, 'utf-8');
const searches: SearchCacheEntry[] = JSON.parse(data);
const found = searches.find(s =>
s.query.toLowerCase() === query.toLowerCase()
);
if (found) {
// Check if cache is less than 24 hours old
const age = Date.now() - new Date(found.timestamp).getTime();
if (age < 24 * 60 * 60 * 1000) {
return found;
}
}
}
} catch (error) {
console.error('❌ Error reading cached search:', error);
}
return null;
}
/**
* Get cache statistics
*/
export function getCacheStats() {
const orders = getCachedOrders();
const products = getAllCachedProducts();
let priceHistoryCount = 0;
if (fs.existsSync(PRICE_HISTORY_CACHE)) {
const data = JSON.parse(fs.readFileSync(PRICE_HISTORY_CACHE, 'utf-8'));
priceHistoryCount = data.length;
}
let searchCount = 0;
if (fs.existsSync(SEARCH_CACHE)) {
const data = JSON.parse(fs.readFileSync(SEARCH_CACHE, 'utf-8'));
searchCount = data.length;
}
return {
totalOrders: orders.length,
totalProducts: products.length,
priceHistoryEntries: priceHistoryCount,
cachedSearches: searchCount,
cacheDirectory: CACHE_DIR
};
}