165 lines
No EOL
6 KiB
TypeScript
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 [];
|
|
}
|
|
} |