freshyo/apps/backend/src/lib/product-store.ts
2026-01-24 12:22:50 +05:30

165 lines
No EOL
6 KiB
TypeScript

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<void> {
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<number, typeof allDeliverySlots>();
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<number, typeof allSpecialDeals>();
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<Product | null> {
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<Product[]> {
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 [];
}
}