import redisClient from './redis-client'; import { db } from '../db/db_index'; import { productInfo, units, productSlots, deliverySlotInfo, specialDeals, storeInfo } from '../db/schema'; import { generateSignedUrlsFromS3Urls } from './s3-client'; import { eq, and, gt, sql } from 'drizzle-orm'; // Uniform Product Type (matches getProductDetails return) interface Product { id: number; name: string; shortDescription: string | null; longDescription: string | null; price: string; marketPrice: string | null; unitNotation: string; images: string[]; isOutOfStock: boolean; store: { id: number; name: string; description: string | null } | null; incrementStep: number; productQuantity: number; isFlashAvailable: boolean; flashPrice: string | null; deliverySlots: Array<{ id: number; deliveryTime: Date; freezeTime: Date }>; specialDeals: Array<{ quantity: string; price: string; validTill: Date }>; } export async function initializeProducts(): Promise { try { console.log('Initializing product store in Redis...'); // Fetch all products with full details (similar to productMega logic) const productsData = await db .select({ id: productInfo.id, name: productInfo.name, shortDescription: productInfo.shortDescription, longDescription: productInfo.longDescription, price: productInfo.price, marketPrice: productInfo.marketPrice, images: productInfo.images, isOutOfStock: productInfo.isOutOfStock, storeId: productInfo.storeId, unitShortNotation: units.shortNotation, incrementStep: productInfo.incrementStep, productQuantity: productInfo.productQuantity, isFlashAvailable: productInfo.isFlashAvailable, flashPrice: productInfo.flashPrice, }) .from(productInfo) .innerJoin(units, eq(productInfo.unitId, units.id)); // Fetch all stores const allStores = await db.query.storeInfo.findMany({ columns: { id: true, name: true, description: true }, }); const storeMap = new Map(allStores.map(s => [s.id, s])); // Fetch all delivery slots const allDeliverySlots = await db .select({ productId: productSlots.productId, id: deliverySlotInfo.id, deliveryTime: deliverySlotInfo.deliveryTime, freezeTime: deliverySlotInfo.freezeTime, }) .from(productSlots) .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id)) .where( and( eq(deliverySlotInfo.isActive, true), gt(deliverySlotInfo.deliveryTime, sql`NOW()`) ) ); const deliverySlotsMap = new Map(); for (const slot of allDeliverySlots) { if (!deliverySlotsMap.has(slot.productId)) deliverySlotsMap.set(slot.productId, []); deliverySlotsMap.get(slot.productId)!.push(slot); } // Fetch all special deals const allSpecialDeals = await db .select({ productId: specialDeals.productId, quantity: specialDeals.quantity, price: specialDeals.price, validTill: specialDeals.validTill, }) .from(specialDeals) .where(gt(specialDeals.validTill, sql`NOW()`)); const specialDealsMap = new Map(); for (const deal of allSpecialDeals) { if (!specialDealsMap.has(deal.productId)) specialDealsMap.set(deal.productId, []); specialDealsMap.get(deal.productId)!.push(deal); } // Store each product in Redis for (const product of productsData) { const signedImages = await generateSignedUrlsFromS3Urls((product.images as string[]) || []); const store = product.storeId ? storeMap.get(product.storeId) || null : null; const deliverySlots = deliverySlotsMap.get(product.id) || []; const specialDeals = specialDealsMap.get(product.id) || []; const productObj: Product = { id: product.id, name: product.name, shortDescription: product.shortDescription, longDescription: product.longDescription, price: product.price.toString(), marketPrice: product.marketPrice?.toString() || null, unitNotation: product.unitShortNotation, images: signedImages, isOutOfStock: product.isOutOfStock, store: store ? { id: store.id, name: store.name, description: store.description } : null, incrementStep: product.incrementStep, productQuantity: product.productQuantity, isFlashAvailable: product.isFlashAvailable, flashPrice: product.flashPrice?.toString() || null, deliverySlots: deliverySlots.map(s => ({ id: s.id, deliveryTime: s.deliveryTime, freezeTime: s.freezeTime })), specialDeals: specialDeals.map(d => ({ quantity: d.quantity.toString(), price: d.price.toString(), validTill: d.validTill })), }; await redisClient.set(`product:${product.id}`, JSON.stringify(productObj)); } console.log('Product store initialized successfully'); } catch (error) { console.error('Error initializing product store:', error); } } export async function getProductById(id: number): Promise { try { const key = `product:${id}`; const data = await redisClient.get(key); if (!data) return null; return JSON.parse(data) as Product; } catch (error) { console.error(`Error getting product ${id}:`, error); return null; } } export async function getAllProducts(): Promise { try { // Get all keys matching the pattern "product:*" const keys = await redisClient.KEYS('product:*'); if (keys.length === 0) return []; // Get all products using MGET for better performance const productsData = await redisClient.MGET(keys); const products: Product[] = []; for (const productData of productsData) { if (productData) { products.push(JSON.parse(productData) as Product); } } return products; } catch (error) { console.error('Error getting all products:', error); return []; } }