diff --git a/AGENTS.md b/AGENTS.md index ae98ac1..feff7cb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,3 +48,6 @@ react-native. They are available in the common-ui as MyText, MyTextInput, MyTouc ## Important Notes - Don't do anything with git. Don't do git add or git commit. That will be managed entirely by the user + +## Change Log +- there should be a change log file. unless explicitly specified, assume change-log.txt as the name of the log file. Ensure the file exists. If it doesn't exist create it. Before changing any file which is not a md file or txt file write the change to that file exactly. Write the diff to it and then start making changes. for every change add the current time stamp. diff --git a/apps/backend/index.ts b/apps/backend/index.ts index 01e61d5..4d04cc4 100755 --- a/apps/backend/index.ts +++ b/apps/backend/index.ts @@ -2,15 +2,12 @@ import 'dotenv/config'; import { serve } from '@hono/node-server'; import initFunc from '@/src/lib/init'; import { createApp } from '@/src/app' -// import signedUrlCache from '@/src/lib/signed-url-cache'; import { seed } from '@/src/lib/seed'; import '@/src/jobs/jobs-index'; seed() initFunc() -// signedUrlCache.loadFromDisk(); // Disabled for Workers compatibility - const app = createApp() serve({ diff --git a/apps/backend/src/lib/axios.ts b/apps/backend/src/lib/axios.ts deleted file mode 100755 index 69cd048..0000000 --- a/apps/backend/src/lib/axios.ts +++ /dev/null @@ -1,10 +0,0 @@ -import axiosParent from "axios"; -import { getPhonePeBaseUrl } from "@/src/lib/env-exporter" - -export const phonepeAxios = axiosParent.create({ - baseURL: getPhonePeBaseUrl(), - timeout: 40000, - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, -}); diff --git a/apps/backend/src/lib/catch-async.ts b/apps/backend/src/lib/catch-async.ts deleted file mode 100755 index 878afc9..0000000 --- a/apps/backend/src/lib/catch-async.ts +++ /dev/null @@ -1,11 +0,0 @@ -// catchAsync is no longer needed with Hono -// Hono handles async errors automatically -// This file is kept for backward compatibility but should be removed in the future - -import { Context } from 'hono'; - -const catchAsync = (fn: (c: Context) => Promise) => { - return fn; -}; - -export default catchAsync; diff --git a/apps/backend/src/lib/delete-image.ts b/apps/backend/src/lib/delete-image.ts deleted file mode 100644 index 5d6c2b0..0000000 --- a/apps/backend/src/lib/delete-image.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { deleteImageUtil, getOriginalUrlFromSignedUrl } from "@/src/lib/s3-client" -import { getS3Url } from "@/src/lib/env-exporter" - -function extractS3Key(url: string): string | null { - try { - // Check if this is a signed URL first and get the original if it is - const originalUrl = getOriginalUrlFromSignedUrl(url) || url; - - // Find the index of '.com/' in the URL - // const comIndex = originalUrl.indexOf(".com/"); - const baseUrlIndex = originalUrl.indexOf(getS3Url()); - - // If '.com/' is found, return everything after it - if (baseUrlIndex !== -1) { - return originalUrl.substring(baseUrlIndex + getS3Url().length); // +5 to skip '.com/' - } - } catch (error) { - console.error("Error extracting key from URL:", error); - } - - // Return null if the pattern isn't found or there was an error - return null; -} - - -export async function deleteS3Image(imageUrl: string) { - try { - // First check if this is a signed URL and get the original if it is - const originalUrl = getOriginalUrlFromSignedUrl(imageUrl) || imageUrl; - - const key = extractS3Key(originalUrl || ""); - - - if (!key) { - throw new Error("Invalid image URL format"); - } - const deleteS3 = await deleteImageUtil({keys: [key] }); - if (!deleteS3) { - throw new Error("Failed to delete image from S3"); - } - } catch (error) { - console.error("Error deleting image from S3:", error); - } -} diff --git a/apps/backend/src/lib/disk-persisted-set.ts b/apps/backend/src/lib/disk-persisted-set.ts deleted file mode 100644 index e0fccec..0000000 --- a/apps/backend/src/lib/disk-persisted-set.ts +++ /dev/null @@ -1,79 +0,0 @@ -// import fs from "fs"; -// import path from "path"; -// -// export class DiskPersistedSet { -// private set: Set; -// private readonly filePath: string; -// private dirty = false; -// -// constructor(filePath: string = "./persister") { -// this.filePath = path.resolve(filePath); -// -// // โœ… Ensure file exists -// if (!fs.existsSync(this.filePath)) { -// fs.writeFileSync(this.filePath, "", "utf8"); -// } -// -// // โœ… Load existing values from file -// const contents = fs.readFileSync(this.filePath, "utf8"); -// this.set = new Set( -// contents.split("\n").map(x => x.trim()).filter(x => x.length > 0) -// ); -// -// this.registerExitHandlers(); -// } -// -// private persist() { -// if (!this.dirty) return; -// fs.writeFileSync(this.filePath, Array.from(this.set).join("\n"), "utf8"); -// this.dirty = false; -// } -// -// private markDirty() { -// this.dirty = true; -// } -// -// add(value: string): void { -// if (!this.set.has(value)) { -// this.set.add(value); -// this.markDirty(); -// this.persist(); -// } -// } -// -// delete(value: string): void { -// if (this.set.delete(value)) { -// this.markDirty(); -// this.persist(); -// } -// } -// -// has(value: string): boolean { -// return this.set.has(value); -// } -// -// values(): string[] { -// return Array.from(this.set); -// } -// -// clear(): void { -// if (this.set.size > 0) { -// this.set.clear(); -// this.markDirty(); -// this.persist(); -// } -// } -// -// private registerExitHandlers() { -// const flush = () => this.persist(); -// -// process.on("exit", flush); -// process.on("SIGINT", () => { flush(); process.exit(); }); -// process.on("SIGTERM", () => { flush(); process.exit(); }); -// process.on("uncaughtException", (err) => { -// console.error("Uncaught exception. Flushing DiskPersistedSet:", err); -// flush(); -// process.exit(1); -// }); -// } -// } diff --git a/apps/backend/src/lib/event-queue.ts b/apps/backend/src/lib/event-queue.ts deleted file mode 100644 index d5baa8c..0000000 --- a/apps/backend/src/lib/event-queue.ts +++ /dev/null @@ -1,13 +0,0 @@ -// import redisClient from '@/src/lib/redis-client' - -export async function enqueue(queueName: string, eventData: any): Promise { - try { - const jsonData = JSON.stringify(eventData); - // const result = await redisClient.lPush(queueName, jsonData); - // return result > 0; - return false; - } catch (error) { - console.error('Event enqueue error:', error); - return false; - } -} \ No newline at end of file diff --git a/apps/backend/src/lib/notif-service.ts b/apps/backend/src/lib/notif-service.ts deleted file mode 100755 index 40baf9f..0000000 --- a/apps/backend/src/lib/notif-service.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { sendPushNotificationsMany } from "@/src/lib/expo-service" -// import { usersTable, notifCredsTable, notificationTable } from "@/src/db/schema"; - -// Core notification dispatch methods (renamed for clarity) -export async function dispatchBulkNotification({ - userIds = [], - pushTokens = [], - title, - body, - data, -}: { - userIds?: number[]; - pushTokens?: string[]; - title: string; - body: string; - data?: Record; -}) { - try { - let allPushTokens: string[] = []; - - // Add provided pushTokens directly - if (pushTokens && pushTokens.length > 0) { - allPushTokens.push(...pushTokens.filter(Boolean)); - } - - // Fetch push tokens for userIds - // if (userIds && userIds.length > 0) { - // const tokensFromDb = await db.query.notifCredsTable.findMany({ - // where: inArray(notifCredsTable.userId, userIds), - // columns: { pushToken: true }, - // }); - // allPushTokens.push( - // ...tokensFromDb.map((t) => t.pushToken).filter(Boolean) - // ); - // } - - // Remove duplicates - allPushTokens = Array.from(new Set(allPushTokens)); - - if (allPushTokens.length === 0) { - console.warn(`No push tokens found for users: ${userIds?.join(", ")}`); - return; - } - - const messages = allPushTokens.map((pushToken) => ({ - pushToken, - title, - body, - data, - })); - await sendPushNotificationsMany(messages); - } catch (error) { - console.error("Error dispatching bulk notifications:", error); - throw new Error("Failed to dispatch notifications"); - } -} - -// Helper to dispatch notification to a single user or pushToken -export async function dispatchUserNotification({ - userId, - pushToken, - title, - body, - data, -}: { - userId?: number; - pushToken?: string; - title: string; - body: string; - data?: Record; -}) { - // Add entry to notificationTable if userId is provided - if (userId) { - try { - // await db.insert(notificationTable).values({ - // userId, - // title, - // body, - // payload: data, - // // addedOn will default to now - // }); - } catch (err) { - console.error('Failed to insert notificationTable entry:', err); - } - } - - await dispatchBulkNotification({ - userIds: userId ? [userId] : [], - pushTokens: pushToken ? [pushToken] : [], - title, - body, - data, - }); -} - -// ============================================================================= -// PURPOSE-SPECIFIC NOTIFICATION METHODS -// ============================================================================= - -// Order-related notifications -export const notifyOrderPlaced = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Order Placed Successfully! ๐ŸŽ‰', - body: `Your order #${orderId} has been placed and is being processed.`, - userId, - data: { orderId, type: 'order_placed' } - }); - -export const notifyOrderConfirmed = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Order Confirmed! โœ…', - body: `Your order #${orderId} has been confirmed and is being prepared.`, - userId, - data: { orderId, type: 'order_confirmed' } - }); - -export const notifyOrderReady = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Order Ready for Pickup! ๐Ÿฝ๏ธ', - body: `Your order #${orderId} is ready for pickup.`, - userId, - data: { orderId, type: 'order_ready' } - }); - -export const notifyOrderCancelled = (orderId: number, userId: number, reason?: string) => - dispatchUserNotification({ - title: 'Order Cancelled โŒ', - body: `Your order #${orderId} has been cancelled.${reason ? ` Reason: ${reason}` : ''}`, - userId, - data: { orderId, type: 'order_cancelled', reason } - }); - -// Delivery notifications -export const notifyDeliveryAssigned = (orderId: number, userId: number, deliveryPerson: string) => - dispatchUserNotification({ - title: 'Delivery Partner Assigned ๐Ÿšš', - body: `${deliveryPerson} will deliver your order #${orderId}.`, - userId, - data: { orderId, deliveryPerson, type: 'delivery_assigned' } - }); - -export const notifyOutForDelivery = (orderId: number, userId: number, eta: string) => - dispatchUserNotification({ - title: 'Out for Delivery! ๐Ÿ“ฆ', - body: `Your order #${orderId} is out for delivery. Expected arrival: ${eta}`, - userId, - data: { orderId, eta, type: 'out_for_delivery' } - }); - -export const notifyDelivered = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Order Delivered! ๐ŸŽŠ', - body: `Your order #${orderId} has been delivered successfully.`, - userId, - data: { orderId, type: 'delivered' } - }); - -// Payment notifications -export const notifyPaymentSuccess = (orderId: number, userId: number, amount: number) => - dispatchUserNotification({ - title: 'Payment Successful ๐Ÿ’ณ', - body: `Payment of โ‚น${amount} for order #${orderId} was successful.`, - userId, - data: { orderId, amount, type: 'payment_success' } - }); - -export const notifyPaymentFailed = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Payment Failed โŒ', - body: `Payment for order #${orderId} failed. Please try again.`, - userId, - data: { orderId, type: 'payment_failed' } - }); - -export const notifyRefundProcessed = (orderId: number, userId: number, amount: number) => - dispatchUserNotification({ - title: 'Refund Processed ๐Ÿ’ฐ', - body: `Refund of โ‚น${amount} for order #${orderId} has been processed.`, - userId, - data: { orderId, amount, type: 'refund_processed' } - }); - -// Promotional notifications -export const notifyNewOffer = (userIds: number[], offerTitle: string, offerDetails: string) => - dispatchBulkNotification({ - title: `New Offer: ${offerTitle} ๐ŸŽ`, - body: offerDetails, - userIds, - data: { type: 'promotion', offerTitle } - }); - -export const notifyFlashSale = (userIds: number[], productName: string, discount: number) => - dispatchBulkNotification({ - title: 'Flash Sale! โšก', - body: `Get ${discount}% off on ${productName}. Limited time offer!`, - userIds, - data: { type: 'flash_sale', productName, discount } - }); - -export const notifyLoyaltyPointsEarned = (userId: number, points: number, reason: string) => - dispatchUserNotification({ - title: 'Loyalty Points Earned! โญ', - body: `You earned ${points} points for ${reason}.`, - userId, - data: { points, reason, type: 'loyalty_points' } - }); - -// Account notifications -export const notifyWelcome = (userId: number, userName: string) => - dispatchUserNotification({ - title: 'Welcome to Meat Farmer! ๐Ÿฅฉ', - body: `Hi ${userName}, welcome to our fresh meat delivery service.`, - userId, - data: { type: 'welcome' } - }); - -export const notifyPasswordReset = (userId: number) => - dispatchUserNotification({ - title: 'Password Reset Successful ๐Ÿ”', - body: 'Your password has been reset successfully.', - userId, - data: { type: 'password_reset' } - }); - -export const notifyAccountVerified = (userId: number) => - dispatchUserNotification({ - title: 'Account Verified! โœ…', - body: 'Your account has been successfully verified.', - userId, - data: { type: 'account_verified' } - }); - -// ============================================================================= -// BACKWARD COMPATIBILITY (DEPRECATED) -// ============================================================================= - -/** - * @deprecated Use notifyOrderConfirmed() or other purpose-specific methods instead - */ -export const sendNotifToSingleUser = dispatchUserNotification; - -/** - * @deprecated Use notifyNewOffer() or other purpose-specific methods instead - */ -export const sendNotifToManyUsers = dispatchBulkNotification; diff --git a/apps/backend/src/lib/redis-client.ts b/apps/backend/src/lib/redis-client.ts deleted file mode 100644 index fb8d855..0000000 --- a/apps/backend/src/lib/redis-client.ts +++ /dev/null @@ -1,133 +0,0 @@ -// import { createClient, RedisClientType } from 'redis'; -import { getRedisUrl } from '@/src/lib/env-exporter' - -const createClient = (args:any) => {} -class RedisClient { - // private client: RedisClientType; - // private subscriberClient: RedisClientType | null = null; - // private isConnected: boolean = false; - // - private client: any; - private subscriberrlient: any; - private isConnected: any = false; - - - constructor() { - this.client = createClient({ - url: getRedisUrl(), - }); - - // this.client.on('error', (err) => { - // console.error('Redis Client Error:', err); - // }); - // - // this.client.on('connect', () => { - // console.log('Redis Client Connected'); - // this.isConnected = true; - // }); - // - // this.client.on('disconnect', () => { - // console.log('Redis Client Disconnected'); - // this.isConnected = false; - // }); - // - // this.client.on('ready', () => { - // console.log('Redis Client Ready'); - // }); - // - // this.client.on('reconnecting', () => { - // console.log('Redis Client Reconnecting'); - // }); - - // Connect immediately (fire and forget) - // this.client.connect().catch((err) => { - // console.error('Failed to connect Redis:', err); - // }); - } - - async set(key: string, value: string, ttlSeconds?: number): Promise { - if (ttlSeconds) { - return await this.client.setEx(key, ttlSeconds, value); - } else { - return await this.client.set(key, value); - } - } - - async get(key: string): Promise { - return await this.client.get(key); - } - - async exists(key: string): Promise { - const result = await this.client.exists(key); - return result === 1; - } - - async delete(key: string): Promise { - return await this.client.del(key); - } - - async lPush(key: string, value: string): Promise { - return await this.client.lPush(key, value); - } - - async KEYS(pattern: string): Promise { - return await this.client.KEYS(pattern); - } - - async MGET(keys: string[]): Promise<(string | null)[]> { - return await this.client.MGET(keys); - } - - // Publish message to a channel - async publish(channel: string, message: string): Promise { - return await this.client.publish(channel, message); - } - - // Subscribe to a channel with callback - async subscribe(channel: string, callback: (message: string) => void): Promise { - // if (!this.subscriberClient) { - // this.subscriberClient = createClient({ - // url: redisUrl, - // }); - // - // this.subscriberClient.on('error', (err) => { - // console.error('Redis Subscriber Error:', err); - // }); - // - // this.subscriberClient.on('connect', () => { - // console.log('Redis Subscriber Connected'); - // }); - // - // await this.subscriberClient.connect(); - // } - // - // await this.subscriberClient.subscribe(channel, callback); - console.log(`Subscribed to channel: ${channel}`); - } - - // Unsubscribe from a channel - async unsubscribe(channel: string): Promise { - // if (this.subscriberClient) { - // await this.subscriberClient.unsubscribe(channel); - // console.log(`Unsubscribed from channel: ${channel}`); - // } - } - - disconnect(): void { - // if (this.isConnected) { - // this.client.disconnect(); - // } - // if (this.subscriberClient) { - // this.subscriberClient.disconnect(); - // } - } - - get isClientConnected(): boolean { - return this.isConnected; - } -} - -const redisClient = new RedisClient(); - -export default redisClient; -export { RedisClient }; diff --git a/apps/backend/src/lib/redisKeyGetters.ts b/apps/backend/src/lib/redisKeyGetters.ts deleted file mode 100644 index c72f66e..0000000 --- a/apps/backend/src/lib/redisKeyGetters.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function getSlotSequenceKey(slotId: number | string): string { - return `slot_sequence_${slotId}`; -} \ No newline at end of file diff --git a/apps/backend/src/lib/signed-url-cache-old.ts b/apps/backend/src/lib/signed-url-cache-old.ts deleted file mode 100755 index 0f4e98c..0000000 --- a/apps/backend/src/lib/signed-url-cache-old.ts +++ /dev/null @@ -1,263 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -const CACHE_FILE_PATH = path.join('.', 'assets', 'signed-url-cache.json'); - -// Interface for cache entries with TTL -interface CacheEntry { - value: string; - expiresAt: number; // Timestamp when this entry expires -} - -class SignedURLCache { - private originalToSignedCache: Map; - private signedToOriginalCache: Map; - - constructor() { - this.originalToSignedCache = new Map(); - this.signedToOriginalCache = new Map(); - - // Create cache directory if it doesn't exist - const cacheDir = path.dirname(CACHE_FILE_PATH); - if (!fs.existsSync(cacheDir)) { - console.log('creating the directory') - - fs.mkdirSync(cacheDir, { recursive: true }); - } - else { - console.log('the directory is already present') - - } - } - - /** - * Get a signed URL from the cache using an original URL as the key - */ - get(originalUrl: string): string | undefined { - const entry = this.originalToSignedCache.get(originalUrl); - - // If no entry or entry has expired, return undefined - if (!entry || Date.now() > entry.expiresAt) { - if (entry) { - // Remove expired entry - this.originalToSignedCache.delete(originalUrl); - // Also remove from reverse mapping if it exists - this.signedToOriginalCache.delete(entry.value); - } - return undefined; - } - - return entry.value; - } - - /** - * Get the original URL from the cache using a signed URL as the key - */ - getOriginalUrl(signedUrl: string): string | undefined { - const entry = this.signedToOriginalCache.get(signedUrl); - - // If no entry or entry has expired, return undefined - if (!entry || Date.now() > entry.expiresAt) { - if (entry) { - // Remove expired entry - this.signedToOriginalCache.delete(signedUrl); - // Also remove from primary mapping if it exists - this.originalToSignedCache.delete(entry.value); - } - return undefined; - } - - return entry.value; - } - - /** - * Set a value in the cache with a TTL (Time To Live) - * @param originalUrl The original S3 URL - * @param signedUrl The signed URL - * @param ttlMs Time to live in milliseconds (default: 3 days) - */ - set(originalUrl: string, signedUrl: string, ttlMs: number = 259200000): void { - const expiresAt = Date.now() + ttlMs; - - const entry: CacheEntry = { - value: signedUrl, - expiresAt - }; - - const reverseEntry: CacheEntry = { - value: originalUrl, - expiresAt - }; - - this.originalToSignedCache.set(originalUrl, entry); - this.signedToOriginalCache.set(signedUrl, reverseEntry); - } - - has(originalUrl: string): boolean { - const entry = this.originalToSignedCache.get(originalUrl); - - // Entry exists and hasn't expired - return !!entry && Date.now() <= entry.expiresAt; - } - - hasSignedUrl(signedUrl: string): boolean { - const entry = this.signedToOriginalCache.get(signedUrl); - - // Entry exists and hasn't expired - return !!entry && Date.now() <= entry.expiresAt; - } - - clear(): void { - this.originalToSignedCache.clear(); - this.signedToOriginalCache.clear(); - this.saveToDisk(); - } - - /** - * Remove all expired entries from the cache - * @returns The number of expired entries that were removed - */ - clearExpired(): number { - const now = Date.now(); - let removedCount = 0; - - // Clear expired entries from original to signed cache - for (const [originalUrl, entry] of this.originalToSignedCache.entries()) { - if (now > entry.expiresAt) { - this.originalToSignedCache.delete(originalUrl); - removedCount++; - } - } - - // Clear expired entries from signed to original cache - for (const [signedUrl, entry] of this.signedToOriginalCache.entries()) { - if (now > entry.expiresAt) { - this.signedToOriginalCache.delete(signedUrl); - // No need to increment removedCount as we've already counted these in the first loop - } - } - - if (removedCount > 0) { - console.log(`SignedURLCache: Cleared ${removedCount} expired entries`); - } - - return removedCount; - } - - /** - * Save the cache to disk - */ - saveToDisk(): void { - try { - // Remove expired entries before saving - const removedCount = this.clearExpired(); - - // Convert Maps to serializable objects - const serializedOriginalToSigned: Record = {}; - const serializedSignedToOriginal: Record = {}; - - for (const [originalUrl, entry] of this.originalToSignedCache.entries()) { - serializedOriginalToSigned[originalUrl] = { - value: entry.value, - expiresAt: entry.expiresAt - }; - } - - for (const [signedUrl, entry] of this.signedToOriginalCache.entries()) { - serializedSignedToOriginal[signedUrl] = { - value: entry.value, - expiresAt: entry.expiresAt - }; - } - - const serializedCache = { - originalToSigned: serializedOriginalToSigned, - signedToOriginal: serializedSignedToOriginal - }; - - // Write to file - fs.writeFileSync( - CACHE_FILE_PATH, - JSON.stringify(serializedCache), - 'utf8' - ); - - console.log(`SignedURLCache: Saved ${this.originalToSignedCache.size} entries to disk`); - } catch (error) { - console.error('Error saving SignedURLCache to disk:', error); - } - } - - /** - * Load the cache from disk - */ - loadFromDisk(): void { - try { - if (fs.existsSync(CACHE_FILE_PATH)) { - // Read from file - const data = fs.readFileSync(CACHE_FILE_PATH, 'utf8'); - - // Parse the data - const parsedData = JSON.parse(data) as { - originalToSigned: Record, - signedToOriginal: Record - }; - - // Only load entries that haven't expired yet - const now = Date.now(); - let loadedCount = 0; - let expiredCount = 0; - - // Load original to signed mappings - if (parsedData.originalToSigned) { - for (const [originalUrl, entry] of Object.entries(parsedData.originalToSigned)) { - if (now <= entry.expiresAt) { - this.originalToSignedCache.set(originalUrl, entry); - loadedCount++; - } else { - expiredCount++; - } - } - } - - // Load signed to original mappings - if (parsedData.signedToOriginal) { - for (const [signedUrl, entry] of Object.entries(parsedData.signedToOriginal)) { - if (now <= entry.expiresAt) { - this.signedToOriginalCache.set(signedUrl, entry); - // Don't increment loadedCount as these are pairs of what we already counted - } else { - // Don't increment expiredCount as these are pairs of what we already counted - } - } - } - - console.log(`SignedURLCache: Loaded ${loadedCount} valid entries from disk (skipped ${expiredCount} expired entries)`); - } else { - console.log('SignedURLCache: No cache file found, starting with empty cache'); - } - } catch (error) { - console.error('Error loading SignedURLCache from disk:', error); - // Start with empty caches if loading fails - this.originalToSignedCache = new Map(); - this.signedToOriginalCache = new Map(); - } - } -} - -// Create a singleton instance to be used throughout the application -const signedUrlCache = new SignedURLCache(); - -process.on('SIGINT', () => { - console.log('SignedURLCache: Saving cache before shutdown...'); - signedUrlCache.saveToDisk(); - process.exit(0); -}); - -process.on('SIGTERM', () => { - console.log('SignedURLCache: Saving cache before shutdown...'); - signedUrlCache.saveToDisk(); - process.exit(0); -}); - -export default signedUrlCache; \ No newline at end of file diff --git a/apps/backend/src/lib/signed-url-cache.ts b/apps/backend/src/lib/signed-url-cache.ts deleted file mode 100644 index 69a391a..0000000 --- a/apps/backend/src/lib/signed-url-cache.ts +++ /dev/null @@ -1,24 +0,0 @@ -// SIGNED URL CACHE - DISABLED -// This file has been disabled to make the backend compatible with Cloudflare Workers. -// File system operations are not available in the Workers environment. -// -// To re-enable caching, migrate to Cloudflare R2 or another object storage solution. -// Original file saved as: signed-url-cache-old.ts -// -// Impact of disabling: -// - S3 signed URLs are generated fresh on every request -// - Increased AWS API calls (higher costs) -// - Slightly slower image loading -// - No file system dependencies (Workers-compatible) - -export default { - get: () => undefined, - set: () => {}, - getOriginalUrl: () => undefined, - has: () => false, - hasSignedUrl: () => false, - clear: () => {}, - clearExpired: () => 0, - saveToDisk: () => {}, - loadFromDisk: () => {}, -}; diff --git a/apps/backend/src/middleware/auth.ts b/apps/backend/src/middleware/auth.ts deleted file mode 100755 index d34c035..0000000 --- a/apps/backend/src/middleware/auth.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Context, Next } from 'hono'; -import { jwtVerify, errors } from 'jose'; -import { ApiError } from '@/src/lib/api-error' -import { getEncodedJwtSecret } from '@/src/lib/env-exporter'; - -export const verifyToken = async (c: Context, next: Next) => { - try { - // Get token from Authorization header - const authHeader = c.req.header('authorization'); - - - if (!authHeader || !authHeader.startsWith('Bearer ')) { - throw new ApiError('Access denied. No token provided', 401); - } - - const token = authHeader.split(' ')[1]; - - if (!token) { - throw new ApiError('Access denied. Invalid token format', 401); - } - - // Verify token - const { payload } = await jwtVerify(token, getEncodedJwtSecret()); - - - // Add user info to context - c.set('user', payload); - - await next(); - } catch (error) { - if (error instanceof errors.JOSEError) { - throw new ApiError('Invalid Auth Credentials', 401); - } else { - throw error; - } - } -}; - -export const requireRole = (roles: string[]) => { - return async (c: Context, next: Next) => { - try { - const user = c.get('user'); - if (!user) { - throw new ApiError('Authentication required', 401); - } - - // Check if user has any of the required roles - const userRoles = user.roles || []; - const hasPermission = roles.some(role => userRoles.includes(role)); - - if (!hasPermission) { - throw new ApiError('Access denied. Insufficient permissions', 403); - } - - await next(); - } catch (error) { - throw error; - } - }; -}; diff --git a/apps/backend/src/postgresImporter.ts b/apps/backend/src/postgresImporter.ts deleted file mode 100644 index 3645f61..0000000 --- a/apps/backend/src/postgresImporter.ts +++ /dev/null @@ -1,281 +0,0 @@ -// // Postgres Importer - Intermediate layer to avoid direct postgresService imports in dbService -// // This file re-exports everything from postgresService -// -// // Re-export database connection -// export { db } from 'postgresService' -// -// // Re-export all schema exports -// export * from 'postgresService' -// -// // Re-export all helper methods from postgresService -// export { -// // Admin - Banner -// getBanners, -// getBannerById, -// createBanner, -// updateBanner, -// deleteBanner, -// // Admin - Complaint -// getComplaints, -// resolveComplaint, -// // Admin - Constants -// getAllConstants, -// upsertConstants, -// // Admin - Coupon -// getAllCoupons, -// getCouponById, -// invalidateCoupon, -// validateCoupon, -// getReservedCoupons, -// getUsersForCoupon, -// createCouponWithRelations, -// updateCouponWithRelations, -// generateCancellationCoupon, -// createReservedCouponWithProducts, -// createCouponForUser, -// checkUsersExist, -// checkCouponExists, -// checkReservedCouponExists, -// getOrderWithUser, -// // Admin - Order -// updateOrderNotes, -// getOrderDetails, -// updateOrderPackaged, -// updateOrderDelivered, -// updateOrderItemPackaging, -// removeDeliveryCharge, -// getSlotOrders, -// updateAddressCoords, -// getAllOrders, -// rebalanceSlots, -// cancelOrder, -// deleteOrderById, -// // Admin - Product -// getAllProducts, -// getProductById, -// deleteProduct, -// createProduct, -// updateProduct, -// checkProductExistsByName, -// checkUnitExists, -// getProductImagesById, -// createSpecialDealsForProduct, -// updateProductDeals, -// replaceProductTags, -// toggleProductOutOfStock, -// updateSlotProducts, -// getSlotsProductIds, -// getAllUnits, -// getAllProductTags, -// getAllProductTagInfos, -// getProductTagInfoById, -// createProductTag, -// getProductTagById, -// updateProductTag, -// deleteProductTag, -// checkProductTagExistsByName, -// getProductReviews, -// respondToReview, -// getAllProductGroups, -// createProductGroup, -// updateProductGroup, -// deleteProductGroup, -// addProductToGroup, -// removeProductFromGroup, -// updateProductPrices, -// // Admin - Slots -// getActiveSlotsWithProducts, -// getActiveSlots, -// getSlotsAfterDate, -// getSlotByIdWithRelations, -// createSlotWithRelations, -// updateSlotWithRelations, -// deleteSlotById, -// updateSlotCapacity, -// getSlotDeliverySequence, -// updateSlotDeliverySequence, -// // Admin - Staff User -// getStaffUserByName, -// getStaffUserById, -// getAllStaff, -// getAllUsers, -// getUserWithDetails, -// updateUserSuspensionStatus, -// checkStaffUserExists, -// checkStaffRoleExists, -// createStaffUser, -// getAllRoles, -// // Admin - Store -// getAllStores, -// getStoreById, -// createStore, -// updateStore, -// deleteStore, -// // Admin - User -// createUserByMobile, -// getUserByMobile, -// getUnresolvedComplaintsCount, -// getAllUsersWithFilters, -// getOrderCountsByUserIds, -// getLastOrdersByUserIds, -// getSuspensionStatusesByUserIds, -// getUserBasicInfo, -// getUserSuspensionStatus, -// getUserOrders, -// getOrderStatusesByOrderIds, -// getItemCountsByOrderIds, -// upsertUserSuspension, -// searchUsers, -// getAllNotifCreds, -// getAllUnloggedTokens, -// getNotifTokensByUserIds, -// getUserIncidentsWithRelations, -// createUserIncident, -// // Admin - Vendor Snippets -// checkVendorSnippetExists, -// getVendorSnippetById, -// getVendorSnippetByCode, -// getAllVendorSnippets, -// createVendorSnippet, -// updateVendorSnippet, -// deleteVendorSnippet, -// getProductsByIds, -// getVendorSlotById, -// getVendorOrdersBySlotId, -// getOrderItemsByOrderIds, -// getOrderStatusByOrderIds, -// updateVendorOrderItemPackaging, -// getVendorOrders, -// // User - Address -// getUserDefaultAddress, -// getUserAddresses, -// getUserAddressById, -// clearUserDefaultAddress, -// createUserAddress, -// updateUserAddress, -// deleteUserAddress, -// hasOngoingOrdersForAddress, -// // User - Banners -// getUserActiveBanners, -// // User - Complaint -// getUserComplaints, -// createUserComplaint, -// // User - Stores -// getUserStoreSummaries, -// getUserStoreDetail, -// // User - Product -// getUserProductDetailById, -// getUserProductReviews, -// getUserProductByIdBasic, -// createUserProductReview, -// getAllProductsWithUnits, -// type ProductSummaryData, -// // User - Slots -// getUserActiveSlotsList, -// getUserProductAvailability, -// // User - Payments -// getUserPaymentOrderById, -// getUserPaymentByOrderId, -// getUserPaymentByMerchantOrderId, -// updateUserPaymentSuccess, -// updateUserOrderPaymentStatus, -// markUserPaymentFailed, -// // User - Auth -// getUserAuthByEmail, -// getUserAuthByMobile, -// getUserAuthById, -// getUserAuthCreds, -// getUserAuthDetails, -// isUserSuspended, -// createUserAuthWithCreds, -// createUserAuthWithMobile, -// upsertUserAuthPassword, -// deleteUserAuthAccount, -// // UV API helpers -// createUserWithProfile, -// getUserDetailsByUserId, -// updateUserProfile, -// // User - Coupon -// getUserActiveCouponsWithRelations, -// getUserAllCouponsWithRelations, -// getUserReservedCouponByCode, -// redeemUserReservedCoupon, -// // User - Profile -// getUserProfileById, -// getUserProfileDetailById, -// getUserWithCreds, -// getUserNotifCred, -// upsertUserNotifCred, -// deleteUserUnloggedToken, -// getUserUnloggedToken, -// upsertUserUnloggedToken, -// // User - Order -// validateAndGetUserCoupon, -// applyDiscountToUserOrder, -// getUserAddressByIdAndUser, -// getOrderProductById, -// checkUserSuspended, -// getUserSlotCapacityStatus, -// placeUserOrderTransaction, -// deleteUserCartItemsForOrder, -// recordUserCouponUsage, -// getUserOrdersWithRelations, -// getUserOrderCount, -// getUserOrderByIdWithRelations, -// getUserCouponUsageForOrder, -// getUserOrderBasic, -// cancelUserOrderTransaction, -// updateUserOrderNotes, -// // Store Helpers -// getAllBannersForCache, -// getAllProductsForCache, -// getAllStoresForCache, -// getAllDeliverySlotsForCache, -// getAllSpecialDealsForCache, -// getAllProductTagsForCache, -// getAllTagsForCache, -// getAllTagProductMappings, -// getAllSlotsWithProductsForCache, -// getAllUserNegativityScores, -// getUserNegativityScore, -// type BannerData, -// type ProductBasicData, -// type StoreBasicData, -// type DeliverySlotData, -// type SpecialDealData, -// type ProductTagData, -// type TagBasicData, -// type TagProductMapping, -// type SlotWithProductsData, -// type UserNegativityData, -// // Automated Jobs -// toggleFlashDeliveryForItems, -// toggleKeyVal, -// getAllKeyValStore, -// // Post-order handler helpers -// getOrdersByIdsWithFullData, -// getOrderByIdWithFullData, -// type OrderWithFullData, -// type OrderWithCancellationData, -// // Common API helpers -// getSuspendedProductIds, -// getNextDeliveryDateWithCapacity, -// getStoresSummary, -// healthCheck, -// // Delete orders helper -// deleteOrdersWithRelations, -// // Seed helpers -// seedUnits, -// seedStaffRoles, -// seedStaffPermissions, -// seedRolePermissions, -// seedKeyValStore, -// type UnitSeedData, -// type RolePermissionAssignment, -// type KeyValSeedData, -// type StaffRoleName, -// type StaffPermissionName, -// // Upload URL Helpers -// createUploadUrlStatus, -// claimUploadUrlStatus, -// } from 'postgresService' diff --git a/apps/backend/src/sqliteImporter.ts b/apps/backend/src/sqliteImporter.ts index 60745fc..070055a 100644 --- a/apps/backend/src/sqliteImporter.ts +++ b/apps/backend/src/sqliteImporter.ts @@ -69,14 +69,10 @@ export { checkProductExistsByName, checkUnitExists, getProductImagesById, - createSpecialDealsForSku, - updateSkuDeals, replaceProductTags, replaceTagProducts, - mergeSkus, updateSlotProducts, getSlotsProductIds, - getAllUnits, getAllProductTags, getAllProductTagInfos, getProductTagInfoById, @@ -91,8 +87,6 @@ export { createProductGroup, updateProductGroup, deleteProductGroup, - addProductToGroup, - removeProductFromGroup, updateProductPrices, // Merge duplicate products into multi-SKU products mergeDuplicateProducts, @@ -114,7 +108,6 @@ export { getAllStaff, getAllUsers, getUserWithDetails, - updateUserSuspensionStatus, checkStaffUserExists, checkStaffRoleExists, createStaffUser, @@ -156,8 +149,6 @@ export { getProductsByIds, getVendorSlotById, getVendorOrdersBySlotId, - getOrderItemsByOrderIds, - getOrderStatusByOrderIds, updateVendorOrderItemPackaging, getVendorOrders, // User - Address @@ -218,7 +209,6 @@ export { getUserProfileById, getUserProfileDetailById, getUserWithCreds, - getUserNotifCred, upsertUserNotifCred, deleteUserUnloggedToken, getUserUnloggedToken, @@ -269,8 +259,6 @@ export { // Post-order handler helpers getOrdersByIdsWithFullData, getOrderByIdWithFullData, - type OrderWithFullData, - type OrderWithCancellationData, // Common API helpers getSuspendedSkuIds, getNextDeliveryDateWithCapacity, diff --git a/apps/backend/src/types/db.types.ts b/apps/backend/src/types/db.types.ts deleted file mode 100644 index 50dbdf9..0000000 --- a/apps/backend/src/types/db.types.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Database Types - Re-exports from shared package -// Central type definitions for backend database operations - -export type { Banner } from '@packages/shared'; diff --git a/change-log.txt b/change-log.txt new file mode 100644 index 0000000..2892936 --- /dev/null +++ b/change-log.txt @@ -0,0 +1,472 @@ +=== Session change log === +Session started: 2026-09-02 +Rule: Before changing any file that is not a .md or .txt file, write the current timestamp followed by the exact diff here first, then make the change. + +(no file changes made yet in this session) + +[2026-09-02 20:34:48] DELETE dead files per DEAD_CODE_REPORT.md ยง1 (28 files): + +Removed in packages/db_helper_sqlite/src/helper_methods/: +- banner.ts (entire file โ€” legacy duplicate, never imported) +- complaint.ts (entire file โ€” legacy duplicate, never imported) +- const.ts (entire file โ€” legacy duplicate, never imported) +- coupon.ts (entire file โ€” legacy duplicate, never imported) +- order.ts (entire file โ€” legacy duplicate, never imported) +- product.ts (entire file โ€” legacy duplicate, never imported) +- slots.ts (entire file โ€” legacy duplicate, never imported) +- staff-user.ts (entire file โ€” legacy duplicate, never imported) +- store.ts (entire file โ€” legacy duplicate, never imported) +- user.ts (entire file โ€” legacy duplicate, never imported) +- vendor-snippets.ts (entire file โ€” legacy duplicate, never imported) + +Removed in packages/db_helper_postgres/src/helper_methods/: (same 11 files as above) +- banner.ts, complaint.ts, const.ts, coupon.ts, order.ts, product.ts, slots.ts, staff-user.ts, store.ts, user.ts, vendor-snippets.ts (entire files โ€” legacy duplicates, never imported) + +Removed in both packages (db_helper_sqlite + db_helper_postgres): +- src/user-apis/tags.ts (entire file โ€” imported by no one) +- src/common-apis/utils.ts (entire file โ€” imported by no one; formatDate/generateCode/calculateDiscount unreferenced) +- src/db/types.ts (entire file โ€” imported by no one) + +Kept: helper_methods/upload-url.ts (alive โ€” re-exported by index.ts, used by backend). + +Command: rm + +[2026-09-02 20:38:02] COMPLETED deletion of 28 dead files (DEAD_CODE_REPORT.md ยง1). +Verification: +- grep for 'helper_methods|user-apis/tags|common-apis/utils|db/types' across both packages + apps/backend: only remaining match is the live import of helper_methods/upload-url.ts in both index.ts files. +- tsc --noEmit on both packages reports pre-existing errors only (in untouched live files: src/db/seed.ts, src/user-apis/order.ts, src/user-apis/product.ts, src/admin-apis/complaint.ts, src/admin-apis/product.ts). No errors reference any deleted module. + +[2026-09-02 20:45:45] DELETE dead exported methods per DEAD_CODE_REPORT.md ยง2a (8 symbols, both packages). + +=== packages/db_helper_sqlite/src/admin-apis/product.ts === +- removed getAllUnits (lines 650-657): + export async function getAllUnits(): Promise { + const allUnits = await db.query.units.findMany({ + orderBy: units.shortNotation, + }) + return allUnits.map(mapUnit) + } +- removed addProductToGroup (lines 1032-1034): + export async function addProductToGroup(groupId: number, productId: number): Promise { + await db.insert(productGroupMembership).values({ groupId, productId }) + } +- removed removeProductFromGroup (lines 1036-1042): + export async function removeProductFromGroup(groupId: number, productId: number): Promise { + await db.delete(productGroupMembership) + .where(and( + eq(productGroupMembership.groupId, groupId), + eq(productGroupMembership.productId, productId) + )) + } + +=== packages/db_helper_postgres/src/admin-apis/product.ts === +- identical removals (getAllUnits at 232-239, addProductToGroup at 496-498, removeProductFromGroup at 500-506) + +=== packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts === +- removed getOrderItemsByOrderIds (lines 196-209): + export async function getOrderItemsByOrderIds(orderIds: number[]) { + return await db.query.orderItems.findMany({ + where: inArray(orderItems.orderId, orderIds), + with: { sku: { with: { product: true, features: true } } }, + }) + } +- removed getOrderStatusByOrderIds (lines 210-214): + export async function getOrderStatusByOrderIds(orderIds: number[]) { + return await db.query.orderStatus.findMany({ + where: inArray(orderStatus.orderId, orderIds), + }) + } + +=== packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts === +- removed getOrderItemsByOrderIds (lines 188-200) โ€” same signature; body joins product.unit instead of sku: + export async function getOrderItemsByOrderIds(orderIds: number[]) { + return await db.query.orderItems.findMany({ + where: inArray(orderItems.orderId, orderIds), + with: { product: { with: { unit: true } } }, + }) + } +- removed getOrderStatusByOrderIds (lines 201-205) โ€” identical to sqlite + +=== packages/db_helper_sqlite/src/admin-apis/staff-user.ts + packages/db_helper_postgres/src/admin-apis/staff-user.ts === +- removed updateUserSuspensionStatus (lines 101-109 in both) โ€” duplicate of live upsertUserSuspension: + export async function updateUserSuspensionStatus(userId: number, isSuspended: boolean): Promise { + await db + .insert(userDetails) + .values({ userId, isSuspended }) + .onConflictDoUpdate({ + target: userDetails.userId, + set: { isSuspended }, + }) + } + +=== packages/db_helper_sqlite/src/user-apis/user.ts + packages/db_helper_postgres/src/user-apis/user.ts === +- removed getUserWithCreds (lines 15-28 in both): + export async function getUserWithCreds(userId: number) { + const result = await db + .select() + .from(users) + .leftJoin(userCreds, eq(users.id, userCreds.userId)) + .where(eq(users.id, userId)) + .limit(1) + if (result.length === 0) return null + return { user: result[0].users, creds: result[0].user_creds } + } + +=== packages/db_helper_sqlite/src/user-apis/auth.ts + packages/db_helper_postgres/src/user-apis/auth.ts === +- removed createUserWithCreds (lines 145-168 in both): + export async function createUserWithCreds(input: { name; email; mobile; hashedPassword }) { + return db.transaction(async (tx) => { ...insert user, insert userCreds, return user... }) + } + +=== packages/db_helper_sqlite/index.ts (export list cleanup) === +- removed line: getAllUnits, +- removed lines: addProductToGroup, + removeProductFromGroup, +- removed lines: getOrderItemsByOrderIds, + getOrderStatusByOrderIds, +- removed line: updateUserSuspensionStatus, +- removed line: getUserWithCreds as getUserWithCreds, +- removed line: createUserWithCreds as createUserAuthWithCreds, + +=== packages/db_helper_postgres/index.ts (export list cleanup) === +- same 8 export-line removals as sqlite index.ts + +=== apps/backend/src/sqliteImporter.ts (re-export chain cleanup) === +- removed line: getAllUnits, +- removed lines: addProductToGroup, + removeProductFromGroup, +- removed lines: getOrderItemsByOrderIds, + getOrderStatusByOrderIds, +- removed line: updateUserSuspensionStatus, +- removed line: createUserAuthWithCreds, +- removed line: getUserWithCreds, + +Note: apps/backend/src/dbService.ts uses `export * from '@/src/sqliteImporter'` โ€” no per-symbol change needed there. +Any imports left unused by these deletions (e.g. productGroupMembership, inArray, userCreds) will be removed in a follow-up entry if tsc/grep confirms they are unused. + +[2026-09-02 20:49:23] FOLLOW-UP: remove imports/orphans left unused by ยง2a deletions. + +=== packages/db_helper_sqlite/src/admin-apis/product.ts === +- removed now-unused local type + mapper (only consumer was deleted getAllUnits): + line 103: type UnitRow = InferSelectModel + line 117: const mapUnit = (unit: UnitRow): AdminUnit => ({ + id: unit.id, + shortNotation: unit.shortNotation, + fullName: unit.fullName, + }) + (postgres product.ts keeps mapUnit โ€” still used there at lines 111, 142) + +=== packages/db_helper_sqlite/src/user-apis/user.ts === +- import line 2: removed 'userCreds' from import { notifCreds, unloggedUserTokens, userCreds, userDetails, users } from '../db/schema' + +=== packages/db_helper_postgres/src/user-apis/user.ts === +- same import fix as sqlite user.ts + +[2026-09-02 20:51:30] COMPLETED ยง2a deletion (8 dead exported methods, both packages + re-export chains). +Removed: getAllUnits, addProductToGroup, removeProductFromGroup, getOrderItemsByOrderIds, getOrderStatusByOrderIds, updateUserSuspensionStatus, getUserWithCreds, createUserWithCreds (+ orphaned mapUnit/UnitRow in sqlite product.ts, orphaned userCreds imports in both user.ts). +Verification: +- grep across packages + apps: zero live references remain (only commented lines in postgresImporter.ts). +- tsc --noEmit: sqlite = 4 errors (same as pre-change baseline), postgres = 40 errors (all pre-existing drizzle inference mismatches in untouched files), backend = 17 errors (all pre-existing, in untouched files). No error references any deleted symbol. + +[2026-09-02 20:54:32] DELETE ยง2b/ยง2c dead exports + PARITY RENAME + RESTORE two methods for future use. + +--- DELETIONS, packages/db_helper_sqlite/src/admin-apis/product.ts --- +- removed createSpecialDealsForSku (lines 1124-1145): inserts specialDeals rows keyed by skuId, returns mapped AdminSpecialDeal[] +- removed updateSkuDeals (lines 1147-1241): diff-based sync of specialDeals for a skuId (add/remove/update validTill) +- removed mergeSkus (lines 1243-1366, end of file): merges fromSkuId into toSkuId across orderItems, specialDeals, cartItems, couponApplicableProducts, JSON skuIds columns (deliverySlotInfo, homeBanners, coupons, reservedCoupons, vendorSnippets), keyValStore popularItems, then deletes skuFeatures + sku + orphaned product + +--- DELETIONS, packages/db_helper_postgres/src/admin-apis/product.ts --- +- removed toggleProductOutOfStock (lines 181-203): flips productInfo.isOutOfStock +- removed createSpecialDealsForProduct (lines 677-698): postgres counterpart of createSpecialDealsForSku (keyed by productId) +- removed updateProductDeals (lines 700-775): postgres counterpart of updateSkuDeals + +--- DELETION, packages/db_helper_postgres/src/lib/automated-jobs.ts --- +- removed toggleFlashDeliveryForItems (lines 10-16): bulk productInfo.isFlashAvailable update +- import line 2: removed 'productInfo' from import { productInfo, keyValStore } from '../db/schema' +- import line 3: removed 'inArray' from import { inArray, eq } from 'drizzle-orm' + +--- EXPORT LIST, packages/db_helper_sqlite/index.ts --- +- removed lines: createSpecialDealsForSku, + updateSkuDeals, + mergeSkus, + +--- EXPORT LIST, packages/db_helper_postgres/index.ts --- +- removed lines: createSpecialDealsForProduct, + updateProductDeals, + toggleProductOutOfStock, +- removed line: toggleFlashDeliveryForItems, + +--- EXPORT LIST, apps/backend/src/sqliteImporter.ts --- +- removed lines: createSpecialDealsForSku, + updateSkuDeals, + mergeSkus, + +--- PARITY RENAME, packages/db_helper_postgres/src/user-apis/product.ts --- +- renamed getSuspendedProductIds -> getSuspendedSkuIds (backend calls getSuspendedSkuIds; sqlite exports that name). + NOTE: implementation unchanged (queries productInfo.isSuspended) โ€” postgres schema has no sku/marketStats model, so this is the closest postgres equivalent of the sqlite query on productMarketStats.skuId. +- packages/db_helper_postgres/index.ts: export line getSuspendedProductIds, -> getSuspendedSkuIds, + +--- RESTORE (user request: needed in future), both packages --- +packages/db_helper_sqlite/src/user-apis/user.ts + packages/db_helper_postgres/src/user-apis/user.ts: +- re-added getUserWithCreds: + export async function getUserWithCreds(userId: number) { + const result = await db + .select() + .from(users) + .leftJoin(userCreds, eq(users.id, userCreds.userId)) + .where(eq(users.id, userId)) + .limit(1) + + if (result.length === 0) return null + return { + user: result[0].users, + creds: result[0].user_creds, + } + } +- re-added 'userCreds' to the schema import line in both files. + +packages/db_helper_sqlite/src/user-apis/auth.ts + packages/db_helper_postgres/src/user-apis/auth.ts: +- re-added createUserWithCreds: + export async function createUserWithCreds(input: { + name: string + email: string + mobile: string + hashedPassword: string + }) { + return db.transaction(async (tx) => { + const [user] = await tx.insert(users).values({ + name: input.name, + email: input.email, + mobile: input.mobile, + }).returning() + + await tx.insert(userCreds).values({ + userId: user.id, + userPassword: input.hashedPassword, + }) + + return user + }) + } + +packages/db_helper_sqlite/index.ts + packages/db_helper_postgres/index.ts: +- re-added export lines: createUserWithCreds as createUserAuthWithCreds, (user auth block) + getUserWithCreds as getUserWithCreds, (user profile block) + +apps/backend/src/sqliteImporter.ts: +- re-added re-export lines: createUserAuthWithCreds, and getUserWithCreds, + +[2026-09-02 20:58:19] FOLLOW-UP: remove orphaned CreateSpecialDealInput (only consumers were the deleted deal functions; zero usages repo-wide). + +=== packages/db_helper_sqlite/src/admin-apis/product.ts === +- removed (line ~1118): + export interface CreateSpecialDealInput { + quantity: number + price: number + validTill: string | Date + } + +=== packages/db_helper_postgres/src/admin-apis/product.ts === +- removed identical interface (line ~647) + +[2026-09-02 20:59:28] COMPLETED ยง2b/ยง2c deletions + parity rename + restoration of createUserWithCreds/getUserWithCreds. +Verification: +- grep: zero references to createSpecialDealsForSku, updateSkuDeals, mergeSkus, createSpecialDealsForProduct, updateProductDeals, toggleProductOutOfStock, toggleFlashDeliveryForItems, getSuspendedProductIds (only commented postgresImporter.ts lines excluded from check). +- Restored methods present in both packages' src + index.ts + backend sqliteImporter.ts. +- Postgres now exports getSuspendedSkuIds (renamed from getSuspendedProductIds) matching sqlite/backend usage. +- tsc --noEmit baselines unchanged: sqlite 4, postgres 40, backend 17 errors โ€” all pre-existing, none reference touched symbols. + +[2026-09-02 21:01:52] ยง3 EXPORT-ONLY DEAD cleanup: remove from index.ts (implementations kept), delete non-exported dead types. + +=== packages/db_helper_sqlite/index.ts === +- removed line 10 block: export { staffRoleEnum, staffPermissionEnum } from './src/db/schema' + (these enum values still flow to consumers via the existing `export * from './src/db/schema'` โ€” the explicit line was redundant) +- user product block: removed type lines type SkuSummary, + type OffersPageData, + type OffersPageProductData, +- store helpers block: removed line type AvailabilityCacheData, +- user order block: removed type lines type OrderWithFullData, + type OrderWithCancellationData, +- SKU Features block: removed lines cleanFeatureValue, + splitQuantityFeature, + type SkuFeatureLike, + (kept composeUnitNotation, composeSkuName โ€” used by backend) + +=== packages/db_helper_postgres/index.ts === +- removed line 11 block: export { staffRoleEnum, staffPermissionEnum } from './src/db/schema'; +- user order block: removed type lines type OrderWithFullData, + type OrderWithCancellationData, + +=== apps/backend/src/sqliteImporter.ts === +- removed type lines type OrderWithFullData, + type OrderWithCancellationData, +- removed line getUserNotifCred, + +Note: getNotifCred index export line in both packages: getNotifCred as getUserNotifCred, โ€” removed (see below). + +=== packages/db_helper_sqlite/index.ts + packages/db_helper_postgres/index.ts === +- user profile block: removed line getNotifCred as getUserNotifCred, + (function getNotifCred kept in user.ts โ€” called internally by upsertNotifCred) + +=== packages/db_helper_sqlite/src/user-apis/order.ts === +- removed dead interface (lines 33-40): + export interface PlaceOrderInput { + userId: number + selectedItems: OrderItemInput[] + addressId: number + paymentMethod: 'online' | 'cod' + couponId?: number + userNotes?: string + isFlash?: boolean + } +- removed dead interface (lines 43-51): + export interface OrderGroupData { + slotId: number | null + items: Array<{ + productId: number + quantity: number + slotId: number | null + product: typeof productInfo.$inferSelect + }> + } + +=== packages/db_helper_postgres/src/user-apis/order.ts === +- removed identical PlaceOrderInput (lines 29-36) and OrderGroupData (lines 39-47) + +[2026-09-02 21:04:10] CORRECTION to previous entry: while editing packages/db_helper_sqlite/index.ts user product block I accidentally removed getAllSkusSummary, getOffersAndCombos and type ProductSummaryData (all three are USED by backend). Immediately restored them; final state of that block keeps: getAllProductsWithUnits, getAllSkusSummary, getOffersAndCombos, type ProductSummaryData. Only type SkuSummary, type OffersPageData, type OffersPageProductData were meant to be (and now are) removed. + +[2026-09-02 21:07:40] CORRECTION: while editing the user order block of packages/db_helper_sqlite/index.ts I accidentally removed getOrdersByIdsWithFullData and getOrderByIdWithFullData (both USED by backend post-order-handler). Immediately restored. Final state: those two functions kept; only type OrderWithFullData and type OrderWithCancellationData removed from that block. Also fixed an earlier accidental newline removal after the store-helpers export close brace. + +[2026-09-02 21:05:24] FOLLOW-UP: remove orphaned OrderItemInput from user-apis/order.ts in both packages (its only consumer, PlaceOrderInput, was deleted; definition-only now). + +=== packages/db_helper_sqlite/src/user-apis/order.ts === +- removed (lines 27-31): + export interface OrderItemInput { + productId: number + quantity: number + slotId: number | null + } + +=== packages/db_helper_postgres/src/user-apis/order.ts === +- removed identical interface (lines 23-27) + +[2026-09-02 21:06:16] COMPLETED ยง3 export-only-dead cleanup. +Removed from index.ts (implementations kept in src): +- both packages: redundant staffRoleEnum/staffPermissionEnum export block (still exported via `export * from './src/db/schema'`), type OrderWithFullData, type OrderWithCancellationData, getNotifCred as getUserNotifCred +- sqlite only: type SkuSummary, type OffersPageData, type OffersPageProductData, type AvailabilityCacheData, cleanFeatureValue, splitQuantityFeature, type SkuFeatureLike (kept composeUnitNotation/composeSkuName) +Removed from apps/backend/src/sqliteImporter.ts: type OrderWithFullData, type OrderWithCancellationData, getUserNotifCred +Deleted dead types in user-apis/order.ts (both packages): PlaceOrderInput, OrderGroupData, OrderItemInput +Verification: +- Backend-used exports confirmed intact: getAllSkusSummary, getOffersAndCombos, ProductSummaryData, getOrdersByIdsWithFullData, getOrderByIdWithFullData, getAvailabilityForCache, getAllProductCombosForCache, getUserWithCreds, createUserAuthWithCreds, composeSkuName, composeUnitNotation +- tsc --noEmit baselines unchanged: sqlite 4 (seed.ts 1, order.ts 2, product.ts 1), postgres 40, backend 17 โ€” all pre-existing, none reference touched symbols. + +[2026-09-02 21:21:13] ยง4: remove never-referenced schema tables โ€” addressZones, addressAreas, userNotifications, productCategories (BOTH packages). +NOTE: this only removes them from the drizzle schema (code). The actual DB tables/columns/FK constraints still exist in D1/Postgres โ€” dropping them in the DB requires a migration, which the user will handle (per AGENTS.md). Both schema.ts files drift from the live DB until then; drizzle-kit push/generate must NOT be run by the agent. + +=== packages/db_helper_sqlite/src/db/schema.ts === +- removed addressZones table (lines 106-110): + export const addressZones = sqliteTable('address_zones', { + id: integer().primaryKey({ autoIncrement: true }), + zoneName: text('zone_name').notNull(), + addedAt: timestampText('added_at').notNull().default(sql`CURRENT_TIMESTAMP`), + }) +- removed addressAreas table (lines 112-117): + export const addressAreas = sqliteTable('address_areas', { + id: integer().primaryKey({ autoIncrement: true }), + placeName: text('place_name').notNull(), + zoneId: integer('zone_id').references(() => addressZones.id), + createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), + }) +- addresses table: changed zoneId column to drop FK (column KEPT โ€” shared UserAddress type and user-apis/address.ts mapUserAddress still expose zoneId): + before: zoneId: integer('zone_id').references(() => addressZones.id), + after: zoneId: integer('zone_id'), +- removed productCategories table (lines 431-434): + export const productCategories = sqliteTable('product_categories', { + id: integer().primaryKey({ autoIncrement: true }), + name: text().notNull(), + description: text(), + }) +- removed userNotifications table (lines 545-551): + export const userNotifications = sqliteTable('user_notifications', { + id: integer().primaryKey({ autoIncrement: true }), + title: text('title').notNull(), + imageUrl: text('image_url'), + createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), + body: text('body').notNull(), + applicableUsers: jsonText('applicable_users'), + }) +- addressesRelations: removed line zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), +- removed productCategoriesRelations (line 686): export const productCategoriesRelations = relations(productCategories, ({}) => ({})) +- removed userNotificationsRelations (lines 720-722): + export const userNotificationsRelations = relations(userNotifications, ({}) => ({ + // No relations needed for now + })) +- removed addressZonesRelations (lines 749-752): + export const addressZonesRelations = relations(addressZones, ({ many }) => ({ + addresses: many(addresses), + areas: many(addressAreas), + })) +- removed addressAreasRelations (lines 754-756): + export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ + zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), + })) + +=== packages/db_helper_postgres/src/db/schema.ts === (same removals, postgres style) +- removed addressZones table (lines 58-62, mf.table('address_zones', ...)) +- removed addressAreas table (lines 64-69, mf.table('address_areas', ...)) +- addresses.zoneId (line 54): dropped .references(() => addressZones.id) โ€” column kept +- removed productCategories table (lines 318-322, mf.table('product_categories', ...)) +- removed userNotifications table (lines 434-440, mf.table('user_notifications', ...)) +- addressesRelations: removed zone relation line (line 496) +- removed productCategoriesRelations (line 573) +- removed userNotificationsRelations (lines 607-609) +- removed addressZonesRelations (lines 636-639) and addressAreasRelations (lines 641-643) + +[2026-09-02 21:23:14] COMPLETED ยง4 schema table removal. +- Removed from both packages' src/db/schema.ts: addressZones, addressAreas, productCategories, userNotifications tables + productCategoriesRelations, userNotificationsRelations, addressZonesRelations, addressAreasRelations. +- addresses.zoneId column KEPT (FK reference + addressesRelations.zone relation removed) because shared UserAddress type and user-apis/address.ts still expose zoneId. +- Verified: zero remaining references to the 4 tables anywhere; jsonText/jsonb/text imports still used by other tables; tsc baselines unchanged (sqlite 4, postgres 40, backend 17 โ€” all pre-existing). +- REMINDER: DB tables still exist in D1/Postgres until the user runs a drop migration (agent must not run drizzle migrations). + +[2026-09-02 21:24:48] ยง6: DELETE dead backend files (10 files) + clean commented references to them. + +DELETED (all verified: zero live imports โ€” only commented mentions remain): +- apps/backend/src/postgresImporter.ts (entirely commented-out swap scaffold; restore from git when swapping to postgres) +- apps/backend/src/lib/signed-url-cache-old.ts +- apps/backend/src/lib/signed-url-cache.ts +- apps/backend/src/lib/disk-persisted-set.ts (body fully commented out) +- apps/backend/src/lib/catch-async.ts +- apps/backend/src/lib/axios.ts (phonepeAxios never imported) +- apps/backend/src/lib/delete-image.ts (deleteS3Image never imported) +- apps/backend/src/lib/event-queue.ts (enqueue never imported; queue-consumer.ts is the live path) +- apps/backend/src/lib/notif-service.ts +- apps/backend/src/types/db.types.ts + +COMMENT CLEANUP (files are .ts โ†’ logged here): +=== apps/backend/index.ts === +- removed line 5: // import signedUrlCache from '@/src/lib/signed-url-cache'; +- removed line 12: // signedUrlCache.loadFromDisk(); // Disabled for Workers compatibility + Left untouched: s3-client.ts:141 prose comment (mentions the concept, not an import), dbService.ts prose header. +KEPT (verified alive): jobs/cache-creator.ts (Durable Object), lib/queue-consumer.ts, lib/flash-delivery-cron.ts, lib/seed.ts, lib/init.ts, lib/worker-init.ts, src/apis/**, src/test-controller.ts, types/hono.d.ts (ambient). + +[2026-09-02 21:25:58] COMPLETED ยง6 backend dead-file deletion (10 files) + index.ts comment cleanup. +Verification: backend tsc --noEmit now reports 11 errors (was 17) โ€” the 6 that vanished were signed-url-cache-old.ts's own pre-existing errors; remaining 11 are the same pre-existing set in untouched files (app.ts, lib/init.ts, s3-client.ts, main-router.ts, trpc-index.ts, sqlite order.ts/product.ts). No new errors. +Note: apps/backend/src/lib/s3-client.ts.txt (a .txt copy of s3-client) still exists โ€” left alone, flagging as possible future cleanup. + +[2026-09-02 21:31:17] Remove 4 dead-code items missed by our report but caught by cmd_v4_dead.md. + +DELETED (3 files, verified zero live imports โ€” only commented references): +- apps/backend/src/lib/redis-client.ts (stubbed: createClient = (args:any) => {}; only '// import redisClient' comments remain elsewhere) +- apps/backend/src/lib/redisKeyGetters.ts (only commented imports in trpc/apis/admin-apis/apis/slots.ts:7,613,694) +- apps/backend/src/middleware/auth.ts (verifyToken/requireRole never imported; app uses middleware/auth.middleware.ts + staff-auth.ts) + +EDITED: +=== packages/shared/index.ts === +- removed line 10 (dead type, definition-only): + export type CacheFilename = typeof CACHE_FILENAMES[keyof typeof CACHE_FILENAMES] + (CACHE_FILENAMES const itself is heavily used by backend cloud_cache.ts and web-ui/user-ui hooks โ€” kept) + +[2026-09-02 21:32:23] COMPLETED removal of 4 missed dead-code items. +- Deleted: apps/backend/src/lib/redis-client.ts, apps/backend/src/lib/redisKeyGetters.ts, apps/backend/src/middleware/auth.ts (middleware/ dir still has auth.middleware.ts + staff-auth.ts). +- Removed CacheFilename type from packages/shared/index.ts (CACHE_FILENAMES const kept โ€” used by cloud_cache.ts + web-ui/user-ui hooks). +- Verified: zero remaining references (excluding comments); backend tsc --noEmit = 11 errors, same pre-existing baseline. diff --git a/packages/db_helper_postgres/index.ts b/packages/db_helper_postgres/index.ts index 09b1a4b..3bec1bd 100644 --- a/packages/db_helper_postgres/index.ts +++ b/packages/db_helper_postgres/index.ts @@ -7,9 +7,6 @@ export { db } from './src/db/db_index'; // Re-export schema export * from './src/db/schema'; -// Export enum types for type safety -export { staffRoleEnum, staffPermissionEnum } from './src/db/schema'; - // Admin API helpers - explicitly namespaced exports to avoid duplicates export { // Banner @@ -80,13 +77,9 @@ export { checkProductExistsByName, checkUnitExists, getProductImagesById, - createSpecialDealsForProduct, - updateProductDeals, replaceProductTags, - toggleProductOutOfStock, updateSlotProducts, getSlotsProductIds, - getAllUnits, getAllProductTags, getAllProductTagInfos, getProductTagInfoById, @@ -101,8 +94,6 @@ export { createProductGroup, updateProductGroup, deleteProductGroup, - addProductToGroup, - removeProductFromGroup, updateProductPrices, } from './src/admin-apis/product'; @@ -128,7 +119,6 @@ export { getAllStaff, getAllUsers, getUserWithDetails, - updateUserSuspensionStatus, checkStaffUserExists, checkStaffRoleExists, createStaffUser, @@ -179,8 +169,6 @@ export { getProductsByIds, getVendorSlotById, getVendorOrdersBySlotId, - getOrderItemsByOrderIds, - getOrderStatusByOrderIds, updateVendorOrderItemPackaging, getVendorOrders, } from './src/admin-apis/vendor-snippets'; @@ -271,7 +259,6 @@ export { getUserById as getUserProfileById, getUserDetailByUserId as getUserProfileDetailById, getUserWithCreds as getUserWithCreds, - getNotifCred as getUserNotifCred, upsertNotifCred as upsertUserNotifCred, deleteUnloggedToken as deleteUserUnloggedToken, getUnloggedToken as getUserUnloggedToken, @@ -299,8 +286,6 @@ export { // Post-order handler helpers getOrdersByIdsWithFullData, getOrderByIdWithFullData, - type OrderWithFullData, - type OrderWithCancellationData, } from './src/user-apis/order'; // Store Helpers (for cache initialization) @@ -335,7 +320,6 @@ export { // Automated Jobs Helpers export { - toggleFlashDeliveryForItems, toggleKeyVal, getAllKeyValStore, } from './src/lib/automated-jobs'; @@ -347,7 +331,7 @@ export { // Common API Helpers export { - getSuspendedProductIds, + getSuspendedSkuIds, getNextDeliveryDateWithCapacity, } from './src/user-apis/product'; diff --git a/packages/db_helper_postgres/src/admin-apis/product.ts b/packages/db_helper_postgres/src/admin-apis/product.ts index 7f6f61f..f935a35 100644 --- a/packages/db_helper_postgres/src/admin-apis/product.ts +++ b/packages/db_helper_postgres/src/admin-apis/product.ts @@ -178,30 +178,6 @@ export async function updateProduct(id: number, updates: ProductInfoUpdate): Pro return mapProduct(product) } -export async function toggleProductOutOfStock(id: number): Promise { - const product = await db.query.productInfo.findFirst({ - where: eq(productInfo.id, id), - }) - - if (!product) { - return null - } - - const [updatedProduct] = await db - .update(productInfo) - .set({ - isOutOfStock: !product.isOutOfStock, - }) - .where(eq(productInfo.id, id)) - .returning() - - if (!updatedProduct) { - return null - } - - return mapProduct(updatedProduct) -} - export async function updateSlotProducts(slotId: string, productIds: string[]): Promise { const slot = await db.query.deliverySlotInfo.findFirst({ where: eq(deliverySlotInfo.id, parseInt(slotId)), @@ -229,14 +205,6 @@ export async function updateSlotProducts(slotId: string, productIds: string[]): } } -export async function getAllUnits(): Promise { - const allUnits = await db.query.units.findMany({ - orderBy: units.shortNotation, - }) - - return allUnits.map(mapUnit) -} - export async function getAllProductTags(): Promise { const tags = await db.query.productTagInfo.findMany({ with: { @@ -493,18 +461,6 @@ export async function deleteProductGroup(id: number): Promise { - await db.insert(productGroupMembership).values({ groupId, productId }) -} - -export async function removeProductFromGroup(groupId: number, productId: number): Promise { - await db.delete(productGroupMembership) - .where(and( - eq(productGroupMembership.groupId, groupId), - eq(productGroupMembership.productId, productId) - )) -} - export async function updateProductPrices(updates: Array<{ productId: number price?: number @@ -688,101 +644,6 @@ export async function getProductImagesById(productId: number): Promise { - if (deals.length === 0) { - return [] - } - - const dealInserts = deals.map((deal) => ({ - productId, - quantity: deal.quantity.toString(), - price: deal.price.toString(), - validTill: new Date(deal.validTill), - })) - - const createdDeals = await db - .insert(specialDeals) - .values(dealInserts) - .returning() - - return createdDeals.map(mapSpecialDeal) -} - -export async function updateProductDeals( - productId: number, - deals: CreateSpecialDealInput[] -): Promise { - if (deals.length === 0) { - await db.delete(specialDeals).where(eq(specialDeals.productId, productId)) - return - } - - const existingDeals = await db.query.specialDeals.findMany({ - where: eq(specialDeals.productId, productId), - }) - - const existingDealsMap = new Map( - existingDeals.map((deal) => [`${deal.quantity}-${deal.price}`, deal]) - ) - const newDealsMap = new Map( - deals.map((deal) => [`${deal.quantity}-${deal.price}`, deal]) - ) - - const dealsToAdd = deals.filter((deal) => { - const key = `${deal.quantity}-${deal.price}` - return !existingDealsMap.has(key) - }) - - const dealsToRemove = existingDeals.filter((deal) => { - const key = `${deal.quantity}-${deal.price}` - return !newDealsMap.has(key) - }) - - const dealsToUpdate = deals.filter((deal) => { - const key = `${deal.quantity}-${deal.price}` - const existing = existingDealsMap.get(key) - const nextValidTill = deal.validTill instanceof Date - ? deal.validTill.toISOString().split('T')[0] - : String(deal.validTill) - return existing && existing.validTill.toISOString().split('T')[0] !== nextValidTill - }) - - if (dealsToRemove.length > 0) { - await db.delete(specialDeals).where( - inArray(specialDeals.id, dealsToRemove.map((deal) => deal.id)) - ) - } - - if (dealsToAdd.length > 0) { - const dealInserts = dealsToAdd.map((deal) => ({ - productId, - quantity: deal.quantity.toString(), - price: deal.price.toString(), - validTill: new Date(deal.validTill), - })) - await db.insert(specialDeals).values(dealInserts) - } - - for (const deal of dealsToUpdate) { - const key = `${deal.quantity}-${deal.price}` - const existingDeal = existingDealsMap.get(key) - if (existingDeal) { - await db.update(specialDeals) - .set({ validTill: new Date(deal.validTill) }) - .where(eq(specialDeals.id, existingDeal.id)) - } - } -} - export async function replaceProductTags(productId: number, tagIds: number[]): Promise { await db.delete(productTags).where(eq(productTags.productId, productId)) diff --git a/packages/db_helper_postgres/src/admin-apis/staff-user.ts b/packages/db_helper_postgres/src/admin-apis/staff-user.ts index b0ecfcf..075a0c9 100644 --- a/packages/db_helper_postgres/src/admin-apis/staff-user.ts +++ b/packages/db_helper_postgres/src/admin-apis/staff-user.ts @@ -98,16 +98,6 @@ export async function getUserWithDetails(userId: number): Promise { return user || null; } -export async function updateUserSuspensionStatus(userId: number, isSuspended: boolean): Promise { - await db - .insert(userDetails) - .values({ userId, isSuspended }) - .onConflictDoUpdate({ - target: userDetails.userId, - set: { isSuspended }, - }); -} - export async function checkStaffUserExists(name: string): Promise { const existingUser = await db.query.staffUsers.findFirst({ where: eq(staffUsers.name, name), diff --git a/packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts b/packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts index 5bd6fcc..cd0ec9c 100644 --- a/packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts +++ b/packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts @@ -185,25 +185,6 @@ export async function getVendorOrders() { }) } -export async function getOrderItemsByOrderIds(orderIds: number[]) { - return await db.query.orderItems.findMany({ - where: inArray(orderItems.orderId, orderIds), - with: { - product: { - with: { - unit: true, - }, - }, - }, - }) -} - -export async function getOrderStatusByOrderIds(orderIds: number[]) { - return await db.query.orderStatus.findMany({ - where: inArray(orderStatus.orderId, orderIds), - }) -} - export async function updateVendorOrderItemPackaging( orderItemId: number, isPackaged: boolean diff --git a/packages/db_helper_postgres/src/common-apis/utils.ts b/packages/db_helper_postgres/src/common-apis/utils.ts deleted file mode 100644 index f41a32c..0000000 --- a/packages/db_helper_postgres/src/common-apis/utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Common utility functions that can be used by both admin and user APIs - -export function formatDate(date: Date): string { - return date.toISOString(); -} - -export function generateCode(prefix: string, length: number = 6): string { - const timestamp = Date.now().toString().slice(-length); - const random = Math.random().toString(36).substring(2, 8).toUpperCase(); - return `${prefix}${timestamp}${random}`; -} - -export function calculateDiscount(amount: number, percent: number, maxDiscount?: number): number { - let discount = (amount * percent) / 100; - if (maxDiscount && discount > maxDiscount) { - discount = maxDiscount; - } - return discount; -} diff --git a/packages/db_helper_postgres/src/db/schema.ts b/packages/db_helper_postgres/src/db/schema.ts index 97687af..5239529 100755 --- a/packages/db_helper_postgres/src/db/schema.ts +++ b/packages/db_helper_postgres/src/db/schema.ts @@ -51,20 +51,7 @@ export const addresses = mf.table('addresses', { googleMapsUrl: varchar('google_maps_url', { length: 500 }), adminLatitude: real('admin_latitude'), adminLongitude: real('admin_longitude'), - zoneId: integer('zone_id').references(() => addressZones.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const addressZones = mf.table('address_zones', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - zoneName: varchar('zone_name', { length: 255 }).notNull(), - addedAt: timestamp('added_at').notNull().defaultNow(), -}); - -export const addressAreas = mf.table('address_areas', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - placeName: varchar('place_name', { length: 255 }).notNull(), - zoneId: integer('zone_id').references(() => addressZones.id), + zoneId: integer('zone_id'), createdAt: timestamp('created_at').notNull().defaultNow(), }); @@ -315,12 +302,6 @@ export const notifications = mf.table('notifications', { createdAt: timestamp('created_at').notNull().defaultNow(), }); -export const productCategories = mf.table('product_categories', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar({ length: 255 }).notNull(), - description: varchar({ length: 500 }), -}); - export const cartItems = mf.table('cart_items', { id: integer().primaryKey().generatedAlwaysAsIdentity(), userId: integer('user_id').notNull().references(() => users.id), @@ -431,15 +412,6 @@ export const unloggedUserTokens = mf.table('unlogged_user_tokens', { lastVerified: timestamp('last_verified'), }); -export const userNotifications = mf.table('user_notifications', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - title: varchar('title', { length: 255 }).notNull(), - imageUrl: varchar('image_url', { length: 500 }), - createdAt: timestamp('created_at').notNull().defaultNow(), - body: text('body').notNull(), - applicableUsers: jsonb('applicable_users'), -}); - export const staffRoles = mf.table('staff_roles', { id: integer().primaryKey().generatedAlwaysAsIdentity(), roleName: staffRoleEnum('role_name').notNull(), @@ -493,7 +465,6 @@ export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({ export const addressesRelations = relations(addresses, ({ one, many }) => ({ user: one(users, { fields: [addresses.userId], references: [users.id] }), orders: many(orders), - zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), })); export const unitsRelations = relations(units, ({ many }) => ({ @@ -570,8 +541,6 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ user: one(users, { fields: [notifications.userId], references: [users.id] }), })); -export const productCategoriesRelations = relations(productCategories, ({}) => ({})); - export const cartItemsRelations = relations(cartItems, ({ one }) => ({ user: one(users, { fields: [cartItems.userId], references: [users.id] }), product: one(productInfo, { fields: [cartItems.productId], references: [productInfo.id] }), @@ -604,10 +573,6 @@ export const notifCredsRelations = relations(notifCreds, ({ one }) => ({ user: one(users, { fields: [notifCreds.userId], references: [users.id] }), })); -export const userNotificationsRelations = relations(userNotifications, ({}) => ({ - // No relations needed for now -})); - export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({ owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }), products: many(productInfo), @@ -633,15 +598,6 @@ export const productReviewsRelations = relations(productReviews, ({ one }) => ({ product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }), })); -export const addressZonesRelations = relations(addressZones, ({ many }) => ({ - addresses: many(addresses), - areas: many(addressAreas), -})); - -export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ - zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), -})); - export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({ memberships: many(productGroupMembership), })); diff --git a/packages/db_helper_postgres/src/db/types.ts b/packages/db_helper_postgres/src/db/types.ts deleted file mode 100755 index 93c1f99..0000000 --- a/packages/db_helper_postgres/src/db/types.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { InferSelectModel } from "drizzle-orm"; -import type { - users, - addresses, - units, - productInfo, - deliverySlotInfo, - specialDeals, - orders, - orderItems, - payments, - notifications, - productCategories, - cartItems, - coupons, -} from "@/src/db/schema"; - -export type User = InferSelectModel; -export type Address = InferSelectModel; -export type Unit = InferSelectModel; -export type ProductInfo = InferSelectModel; -export type DeliverySlotInfo = InferSelectModel; -export type SpecialDeal = InferSelectModel; -export type Order = InferSelectModel; -export type OrderItem = InferSelectModel; -export type Payment = InferSelectModel; -export type Notification = InferSelectModel; -export type ProductCategory = InferSelectModel; -export type CartItem = InferSelectModel; -export type Coupon = InferSelectModel; - -// Combined types -export type ProductWithUnit = ProductInfo & { - unit: Unit; -}; - -export type OrderWithItems = Order & { - items: (OrderItem & { product: ProductInfo })[]; - address: Address; - slot: DeliverySlotInfo; -}; - -export type CartItemWithProduct = CartItem & { - product: ProductInfo; -}; diff --git a/packages/db_helper_postgres/src/helper_methods/banner.ts b/packages/db_helper_postgres/src/helper_methods/banner.ts deleted file mode 100644 index 23b6f56..0000000 --- a/packages/db_helper_postgres/src/helper_methods/banner.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { db } from '../db/db_index'; -import { homeBanners } from '../db/schema'; -import { desc, eq } from 'drizzle-orm'; - -export interface Banner { - id: number; - name: string; - imageUrl: string; - description: string | null; - productIds: number[] | null; - redirectUrl: string | null; - serialNum: number | null; - isActive: boolean; - createdAt: Date; - lastUpdated: Date; -} - -export async function getBanners(): Promise { - const banners = await db.query.homeBanners.findMany({ - orderBy: desc(homeBanners.createdAt), - }); - - return banners.map((banner) => ({ - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - })); -} - -export async function getBannerById(id: number): Promise { - const banner = await db.query.homeBanners.findFirst({ - where: eq(homeBanners.id, id), - }); - - if (!banner) return null; - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - }; -} - -export type CreateBannerInput = Omit; - -export async function createBanner(input: CreateBannerInput): Promise { - const [banner] = await db.insert(homeBanners).values({ - name: input.name, - imageUrl: input.imageUrl, - description: input.description, - productIds: input.productIds, - redirectUrl: input.redirectUrl, - serialNum: input.serialNum, - isActive: input.isActive, - }).returning(); - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - }; -} - -export type UpdateBannerInput = Partial>; - -export async function updateBanner(id: number, input: UpdateBannerInput): Promise { - const [banner] = await db.update(homeBanners) - .set({ - ...input, - lastUpdated: new Date(), - }) - .where(eq(homeBanners.id, id)) - .returning(); - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - }; -} - -export async function deleteBanner(id: number): Promise { - await db.delete(homeBanners).where(eq(homeBanners.id, id)); -} diff --git a/packages/db_helper_postgres/src/helper_methods/complaint.ts b/packages/db_helper_postgres/src/helper_methods/complaint.ts deleted file mode 100644 index 0e2cec8..0000000 --- a/packages/db_helper_postgres/src/helper_methods/complaint.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { db } from '../db/db_index'; -import { complaints, users } from '../db/schema'; -import { eq, desc, lt } from 'drizzle-orm'; - -export interface Complaint { - id: number; - complaintBody: string; - userId: number; - orderId: number | null; - isResolved: boolean; - response: string | null; - createdAt: Date; - images: string[] | null; -} - -export interface ComplaintWithUser extends Complaint { - userName: string | null; - userMobile: string | null; -} - -export async function getComplaints( - cursor?: number, - limit: number = 20 -): Promise<{ complaints: ComplaintWithUser[]; hasMore: boolean }> { - let whereCondition = cursor ? lt(complaints.id, cursor) : undefined; - - const complaintsData = await db - .select({ - id: complaints.id, - complaintBody: complaints.complaintBody, - userId: complaints.userId, - orderId: complaints.orderId, - isResolved: complaints.isResolved, - response: complaints.response, - createdAt: complaints.createdAt, - images: complaints.images, - userName: users.name, - userMobile: users.mobile, - }) - .from(complaints) - .leftJoin(users, eq(complaints.userId, users.id)) - .where(whereCondition) - .orderBy(desc(complaints.id)) - .limit(limit + 1); - - const hasMore = complaintsData.length > limit; - const complaintsToReturn = hasMore ? complaintsData.slice(0, limit) : complaintsData; - - return { - complaints: complaintsToReturn.map((c) => ({ - id: c.id, - complaintBody: c.complaintBody, - userId: c.userId, - orderId: c.orderId, - isResolved: c.isResolved, - response: c.response, - createdAt: c.createdAt, - images: c.images, - userName: c.userName, - userMobile: c.userMobile, - })), - hasMore, - }; -} - -export async function resolveComplaint( - id: number, - response?: string -): Promise { - await db - .update(complaints) - .set({ isResolved: true, response }) - .where(eq(complaints.id, id)); -} diff --git a/packages/db_helper_postgres/src/helper_methods/const.ts b/packages/db_helper_postgres/src/helper_methods/const.ts deleted file mode 100644 index 9d3ba86..0000000 --- a/packages/db_helper_postgres/src/helper_methods/const.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { db } from '../db/db_index'; -import { keyValStore } from '../db/schema'; - -export interface Constant { - key: string; - value: any; -} - -export async function getAllConstants(): Promise { - const constants = await db.select().from(keyValStore); - - return constants.map(c => ({ - key: c.key, - value: c.value, - })); -} - -export async function upsertConstants(constants: Constant[]): Promise { - await db.transaction(async (tx) => { - for (const { key, value } of constants) { - await tx.insert(keyValStore) - .values({ key, value }) - .onConflictDoUpdate({ - target: keyValStore.key, - set: { value }, - }); - } - }); -} diff --git a/packages/db_helper_postgres/src/helper_methods/coupon.ts b/packages/db_helper_postgres/src/helper_methods/coupon.ts deleted file mode 100644 index b87eee5..0000000 --- a/packages/db_helper_postgres/src/helper_methods/coupon.ts +++ /dev/null @@ -1,633 +0,0 @@ -import { db } from '../db/db_index'; -import { coupons, reservedCoupons, users } from '../db/schema'; -import { eq, and, like, or, inArray, lt, desc } from 'drizzle-orm'; - -export interface Coupon { - id: number; - couponCode: string; - isUserBased: boolean; - discountPercent: string | null; - flatDiscount: string | null; - minOrder: string | null; - productIds: number[] | null; - maxValue: string | null; - isApplyForAll: boolean; - validTill: Date | null; - maxLimitForUser: number | null; - exclusiveApply: boolean; - isInvalidated: boolean; - createdAt: Date; - createdBy: number; -} - -export async function getAllCoupons( - cursor?: number, - limit: number = 50, - search?: string -): Promise<{ coupons: any[]; hasMore: boolean }> { - let whereCondition = undefined; - const conditions = []; - - if (cursor) { - conditions.push(lt(coupons.id, cursor)); - } - - if (search && search.trim()) { - conditions.push(like(coupons.couponCode, `%${search}%`)); - } - - if (conditions.length > 0) { - whereCondition = and(...conditions); - } - - const result = await db.query.coupons.findMany({ - where: whereCondition, - with: { - creator: true, - applicableUsers: { - with: { - user: true, - }, - }, - applicableProducts: { - with: { - product: true, - }, - }, - }, - orderBy: (coupons, { desc }) => [desc(coupons.createdAt)], - limit: limit + 1, - }); - - const hasMore = result.length > limit; - const couponsList = hasMore ? result.slice(0, limit) : result; - - return { coupons: couponsList, hasMore }; -} - -export async function getCouponById(id: number): Promise { - const result = await db.query.coupons.findFirst({ - where: eq(coupons.id, id), - with: { - creator: true, - applicableUsers: { - with: { - user: true, - }, - }, - applicableProducts: { - with: { - product: true, - }, - }, - }, - }); - - return result || null; -} - -export async function invalidateCoupon(id: number): Promise { - const result = await db.update(coupons) - .set({ isInvalidated: true }) - .where(eq(coupons.id, id)) - .returning(); - - return result[0]; -} - -export interface CouponValidationResult { - valid: boolean; - message?: string; - discountAmount?: number; - coupon?: Partial; -} - -export async function validateCoupon( - code: string, - userId: number, - orderAmount: number -): Promise { - const coupon = await db.query.coupons.findFirst({ - where: and( - eq(coupons.couponCode, code.toUpperCase()), - eq(coupons.isInvalidated, false) - ), - }); - - if (!coupon) { - return { valid: false, message: "Coupon not found or invalidated" }; - } - - // Check expiry date - if (coupon.validTill && new Date(coupon.validTill) < new Date()) { - return { valid: false, message: "Coupon has expired" }; - } - - // Check if coupon applies to all users or specific user - if (!coupon.isApplyForAll && !coupon.isUserBased) { - return { valid: false, message: "Coupon is not available for use" }; - } - - // Check minimum order amount - const minOrderValue = coupon.minOrder ? parseFloat(coupon.minOrder) : 0; - if (minOrderValue > 0 && orderAmount < minOrderValue) { - return { valid: false, message: `Minimum order amount is ${minOrderValue}` }; - } - - // Calculate discount - let discountAmount = 0; - if (coupon.discountPercent) { - const percent = parseFloat(coupon.discountPercent); - discountAmount = (orderAmount * percent) / 100; - } else if (coupon.flatDiscount) { - discountAmount = parseFloat(coupon.flatDiscount); - } - - // Apply max value limit - const maxValueLimit = coupon.maxValue ? parseFloat(coupon.maxValue) : 0; - if (maxValueLimit > 0 && discountAmount > maxValueLimit) { - discountAmount = maxValueLimit; - } - - return { - valid: true, - discountAmount, - coupon: { - id: coupon.id, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - maxValue: coupon.maxValue, - } - }; -} - -export async function getReservedCoupons( - cursor?: number, - limit: number = 50, - search?: string -): Promise<{ coupons: any[]; hasMore: boolean }> { - let whereCondition = undefined; - const conditions = []; - - if (cursor) { - conditions.push(lt(reservedCoupons.id, cursor)); - } - - if (search && search.trim()) { - conditions.push(or( - like(reservedCoupons.secretCode, `%${search}%`), - like(reservedCoupons.couponCode, `%${search}%`) - )); - } - - if (conditions.length > 0) { - whereCondition = and(...conditions); - } - - const result = await db.query.reservedCoupons.findMany({ - where: whereCondition, - with: { - redeemedUser: true, - creator: true, - }, - orderBy: (reservedCoupons, { desc }) => [desc(reservedCoupons.createdAt)], - limit: limit + 1, - }); - - const hasMore = result.length > limit; - const couponsList = hasMore ? result.slice(0, limit) : result; - - return { coupons: couponsList, hasMore }; -} - -export interface UserMiniInfo { - id: number; - name: string; - mobile: string | null; -} - -export async function getUsersForCoupon( - search?: string, - limit: number = 20, - offset: number = 0 -): Promise<{ users: UserMiniInfo[] }> { - let whereCondition = undefined; - if (search && search.trim()) { - whereCondition = or( - like(users.name, `%${search}%`), - like(users.mobile, `%${search}%`) - ); - } - - const userList = await db.query.users.findMany({ - where: whereCondition, - columns: { - id: true, - name: true, - mobile: true, - }, - limit: limit, - offset: offset, - orderBy: (users, { asc }) => [asc(users.name)], - }); - - return { - users: userList.map(user => ({ - id: user.id, - name: user.name || 'Unknown', - mobile: user.mobile, - })) - }; -} - -// ============================================================================ -// BATCH 2: Transaction Methods -// ============================================================================ - -import { couponApplicableUsers, couponApplicableProducts, orders, orderStatus } from '../db/schema'; - -export interface CreateCouponInput { - couponCode: string; - isUserBased: boolean; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - productIds?: number[] | null; - maxValue?: string; - isApplyForAll: boolean; - validTill?: Date; - maxLimitForUser?: number; - exclusiveApply: boolean; - createdBy: number; -} - -export async function createCouponWithRelations( - input: CreateCouponInput, - applicableUsers?: number[], - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode: input.couponCode, - isUserBased: input.isUserBased, - discountPercent: input.discountPercent, - flatDiscount: input.flatDiscount, - minOrder: input.minOrder, - productIds: input.productIds, - createdBy: input.createdBy, - maxValue: input.maxValue, - isApplyForAll: input.isApplyForAll, - validTill: input.validTill, - maxLimitForUser: input.maxLimitForUser, - exclusiveApply: input.exclusiveApply, - }).returning(); - - // Insert applicable users - if (applicableUsers && applicableUsers.length > 0) { - await tx.insert(couponApplicableUsers).values( - applicableUsers.map(userId => ({ - couponId: coupon.id, - userId, - })) - ); - } - - // Insert applicable products - if (applicableProducts && applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(productId => ({ - couponId: coupon.id, - productId, - })) - ); - } - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - productIds: coupon.productIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export interface UpdateCouponInput { - couponCode?: string; - isUserBased?: boolean; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - productIds?: number[] | null; - maxValue?: string; - isApplyForAll?: boolean; - validTill?: Date | null; - maxLimitForUser?: number; - exclusiveApply?: boolean; - isInvalidated?: boolean; -} - -export async function updateCouponWithRelations( - id: number, - input: UpdateCouponInput, - applicableUsers?: number[], - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - // Update the coupon - const [coupon] = await tx.update(coupons) - .set({ - ...input, - lastUpdated: new Date(), - }) - .where(eq(coupons.id, id)) - .returning(); - - // Update applicable users: delete existing and insert new - if (applicableUsers !== undefined) { - await tx.delete(couponApplicableUsers).where(eq(couponApplicableUsers.couponId, id)); - if (applicableUsers.length > 0) { - await tx.insert(couponApplicableUsers).values( - applicableUsers.map(userId => ({ - couponId: id, - userId, - })) - ); - } - } - - // Update applicable products: delete existing and insert new - if (applicableProducts !== undefined) { - await tx.delete(couponApplicableProducts).where(eq(couponApplicableProducts.couponId, id)); - if (applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(productId => ({ - couponId: id, - productId, - })) - ); - } - } - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - productIds: coupon.productIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export async function generateCancellationCoupon( - orderId: number, - staffUserId: number, - userId: number, - orderAmount: number, - couponCode: string -): Promise { - return await db.transaction(async (tx) => { - // Calculate expiry date (30 days from now) - const expiryDate = new Date(); - expiryDate.setDate(expiryDate.getDate() + 30); - - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode, - isUserBased: true, - flatDiscount: orderAmount.toString(), - minOrder: orderAmount.toString(), - maxValue: orderAmount.toString(), - validTill: expiryDate, - maxLimitForUser: 1, - createdBy: staffUserId, - isApplyForAll: false, - }).returning(); - - // Insert applicable users - await tx.insert(couponApplicableUsers).values({ - couponId: coupon.id, - userId, - }); - - // Update order_status with refund coupon ID - await tx.update(orderStatus) - .set({ refundCouponId: coupon.id }) - .where(eq(orderStatus.orderId, orderId)); - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - productIds: coupon.productIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export interface CreateReservedCouponInput { - secretCode: string; - couponCode: string; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - productIds?: number[] | null; - maxValue?: string; - validTill?: Date; - maxLimitForUser?: number; - exclusiveApply: boolean; - createdBy: number; -} - -export async function createReservedCouponWithProducts( - input: CreateReservedCouponInput, - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - const [coupon] = await tx.insert(reservedCoupons).values({ - secretCode: input.secretCode, - couponCode: input.couponCode, - discountPercent: input.discountPercent, - flatDiscount: input.flatDiscount, - minOrder: input.minOrder, - productIds: input.productIds, - maxValue: input.maxValue, - validTill: input.validTill, - maxLimitForUser: input.maxLimitForUser, - exclusiveApply: input.exclusiveApply, - createdBy: input.createdBy, - }).returning(); - - // Insert applicable products if provided - if (applicableProducts && applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(productId => ({ - couponId: coupon.id, - productId, - })) - ); - } - - return coupon; - }); -} - -export async function getOrCreateUserByMobile( - mobile: string -): Promise<{ id: number; mobile: string; name: string | null }> { - return await db.transaction(async (tx) => { - // Check if user exists - let user = await tx.query.users.findFirst({ - where: eq(users.mobile, mobile), - }); - - if (!user) { - // Create new user - const [newUser] = await tx.insert(users).values({ - name: null, - email: null, - mobile, - }).returning(); - user = newUser; - } - - return { - id: user.id, - mobile: user.mobile, - name: user.name, - }; - }); -} - -export async function createCouponForUser( - mobile: string, - couponCode: string, - staffUserId: number -): Promise<{ coupon: Coupon; user: { id: number; mobile: string; name: string | null } }> { - return await db.transaction(async (tx) => { - // Get or create user - let user = await tx.query.users.findFirst({ - where: eq(users.mobile, mobile), - }); - - if (!user) { - const [newUser] = await tx.insert(users).values({ - name: null, - email: null, - mobile, - }).returning(); - user = newUser; - } - - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode, - isUserBased: true, - discountPercent: "20", - minOrder: "1000", - maxValue: "500", - maxLimitForUser: 1, - isApplyForAll: false, - exclusiveApply: false, - createdBy: staffUserId, - validTill: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days from now - }).returning(); - - // Associate coupon with user - await tx.insert(couponApplicableUsers).values({ - couponId: coupon.id, - userId: user.id, - }); - - return { - coupon: { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - productIds: coupon.productIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }, - user: { - id: user.id, - mobile: user.mobile, - name: user.name, - }, - }; - }); -} - -// ============================================================================ -// Utility Functions -// ============================================================================ - -export async function checkUsersExist(userIds: number[]): Promise { - const existingUsers = await db.query.users.findMany({ - where: inArray(users.id, userIds), - columns: { id: true }, - }); - return existingUsers.length === userIds.length; -} - -export async function checkCouponExists(couponCode: string): Promise { - const existing = await db.query.coupons.findFirst({ - where: eq(coupons.couponCode, couponCode), - }); - return !!existing; -} - -export async function checkReservedCouponExists(secretCode: string): Promise { - const existing = await db.query.reservedCoupons.findFirst({ - where: eq(reservedCoupons.secretCode, secretCode), - }); - return !!existing; -} - -export async function getOrderWithUser(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - user: true, - }, - }); -} diff --git a/packages/db_helper_postgres/src/helper_methods/order.ts b/packages/db_helper_postgres/src/helper_methods/order.ts deleted file mode 100644 index 355a9d2..0000000 --- a/packages/db_helper_postgres/src/helper_methods/order.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { db } from '../db/db_index'; -import { orders, orderItems, orderStatus, users, addresses, refunds, complaints, payments } from '../db/schema'; -import { eq, and, gte, lt, desc, inArray, sql } from 'drizzle-orm'; - -export async function updateOrderNotes(orderId: number, adminNotes: string | null): Promise { - const [result] = await db - .update(orders) - .set({ adminNotes }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} - -export async function getOrderWithDetails(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - orderItems: { - with: { - product: { - with: { - unit: true, - }, - }, - }, - }, - user: true, - address: true, - orderStatus: true, - slot: true, - payments: true, - refunds: true, - }, - }); -} - -export async function getFullOrder(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - orderItems: { - with: { - product: { - with: { - unit: true, - }, - }, - }, - }, - user: { - with: { - userDetails: true, - }, - }, - address: true, - orderStatus: true, - slot: true, - payments: true, - refunds: true, - complaints: true, - }, - }); -} - -export async function getOrderDetails(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - orderItems: { - with: { - product: { - with: { - unit: true, - }, - }, - }, - }, - user: true, - address: true, - orderStatus: true, - slot: true, - payments: true, - refunds: true, - complaints: true, - }, - }); -} - -export async function getAllOrders( - limit: number, - cursor?: number, - slotId?: number | null, - filters?: any -): Promise<{ orders: any[]; hasMore: boolean }> { - let whereConditions = []; - - if (cursor) { - whereConditions.push(lt(orders.id, cursor)); - } - - if (slotId) { - whereConditions.push(eq(orders.slotId, slotId)); - } - - // Add filter conditions - if (filters) { - if (filters.packagedFilter === 'packaged') { - whereConditions.push(eq(orders.isPackaged, true)); - } else if (filters.packagedFilter === 'not_packaged') { - whereConditions.push(eq(orders.isPackaged, false)); - } - - if (filters.deliveredFilter === 'delivered') { - whereConditions.push(eq(orders.isDelivered, true)); - } else if (filters.deliveredFilter === 'not_delivered') { - whereConditions.push(eq(orders.isDelivered, false)); - } - - if (filters.flashDeliveryFilter === 'flash') { - whereConditions.push(eq(orders.isFlashDelivery, true)); - } else if (filters.flashDeliveryFilter === 'regular') { - whereConditions.push(eq(orders.isFlashDelivery, false)); - } - } - - const ordersList = await db.query.orders.findMany({ - where: whereConditions.length > 0 ? and(...whereConditions) : undefined, - with: { - orderItems: { - with: { - product: true, - }, - }, - user: true, - orderStatus: true, - slot: true, - }, - orderBy: desc(orders.id), - limit: limit + 1, - }); - - const hasMore = ordersList.length > limit; - return { orders: hasMore ? ordersList.slice(0, limit) : ordersList, hasMore }; -} - -export async function getOrdersBySlotId(slotId: number): Promise { - return await db.query.orders.findMany({ - where: eq(orders.slotId, slotId), - with: { - orderItems: { - with: { - product: true, - }, - }, - user: true, - orderStatus: true, - address: true, - }, - orderBy: desc(orders.createdAt), - }); -} - -export async function updateOrderPackaged(orderId: number, isPackaged: boolean): Promise { - const [result] = await db - .update(orders) - .set({ isPackaged }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} - -export async function updateOrderDelivered(orderId: number, isDelivered: boolean): Promise { - const [result] = await db - .update(orders) - .set({ isDelivered }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} - -export async function updateOrderItemPackaging( - orderItemId: number, - isPackaged: boolean, - isPackageVerified: boolean -): Promise { - await db.update(orderItems) - .set({ is_packaged: isPackaged, is_package_verified: isPackageVerified }) - .where(eq(orderItems.id, orderItemId)); -} - -export async function updateAddressCoords(addressId: number, lat: number, lng: number): Promise { - await db.update(addresses) - .set({ lat, lng }) - .where(eq(addresses.id, addressId)); -} - -export async function getOrderStatus(orderId: number): Promise { - return await db.query.orderStatus.findFirst({ - where: eq(orderStatus.orderId, orderId), - }); -} - -export async function cancelOrder(orderId: number, reason: string): Promise { - return await db.transaction(async (tx) => { - // Update order status - const [order] = await tx.update(orders) - .set({ isCancelled: true, cancellationReason: reason }) - .where(eq(orders.id, orderId)) - .returning(); - - // Create order status entry - await tx.insert(orderStatus).values({ - orderId, - isCancelled: true, - cancelReason: reason, - }); - - return order; - }); -} - -export async function getTodaysOrders(slotId?: number): Promise { - const today = new Date(); - today.setHours(0, 0, 0, 0); - const tomorrow = new Date(today); - tomorrow.setDate(tomorrow.getDate() + 1); - - let whereConditions = [ - gte(orders.createdAt, today), - lt(orders.createdAt, tomorrow), - ]; - - if (slotId) { - whereConditions.push(eq(orders.slotId, slotId)); - } - - return await db.query.orders.findMany({ - where: and(...whereConditions), - with: { - orderItems: { - with: { - product: true, - }, - }, - user: true, - orderStatus: true, - }, - orderBy: desc(orders.createdAt), - }); -} - -export async function removeDeliveryCharge(orderId: number): Promise { - const [result] = await db - .update(orders) - .set({ deliveryCharge: '0' }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} diff --git a/packages/db_helper_postgres/src/helper_methods/product.ts b/packages/db_helper_postgres/src/helper_methods/product.ts deleted file mode 100644 index a9eb10b..0000000 --- a/packages/db_helper_postgres/src/helper_methods/product.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { db } from '../db/db_index'; -import { productInfo, units, specialDeals, productTags, productReviews, productGroupInfo, productGroupMembership } from '../db/schema'; -import { eq, and, inArray, desc, sql, asc } from 'drizzle-orm'; - -export async function getAllProducts(): Promise { - return await db.query.productInfo.findMany({ - orderBy: productInfo.name, - with: { - unit: true, - store: true, - }, - }); -} - -export async function getProductById(id: number): Promise { - return await db.query.productInfo.findFirst({ - where: eq(productInfo.id, id), - with: { - unit: true, - store: true, - specialDeals: true, - productTags: { - with: { - tag: true, - }, - }, - }, - }); -} - -export async function createProduct(input: any): Promise { - const [product] = await db.insert(productInfo).values(input).returning(); - return product; -} - -export async function updateProduct(id: number, updates: any): Promise { - const [product] = await db.update(productInfo) - .set(updates) - .where(eq(productInfo.id, id)) - .returning(); - return product; -} - -export async function toggleProductOutOfStock(id: number, isOutOfStock: boolean): Promise { - const [product] = await db.update(productInfo) - .set({ isOutOfStock }) - .where(eq(productInfo.id, id)) - .returning(); - return product; -} - -export async function getAllUnits(): Promise { - return await db.query.units.findMany({ - orderBy: units.name, - }); -} - -export async function getAllProductTags(): Promise { - return await db.query.productTags.findMany({ - with: { - products: { - with: { - product: true, - }, - }, - }, - }); -} - -export async function getProductReviews(productId: number): Promise { - return await db.query.productReviews.findMany({ - where: eq(productReviews.productId, productId), - with: { - user: true, - }, - orderBy: desc(productReviews.createdAt), - }); -} - -export async function respondToReview(reviewId: number, adminResponse: string): Promise { - await db.update(productReviews) - .set({ adminResponse }) - .where(eq(productReviews.id, reviewId)); -} - -export async function getAllProductGroups(): Promise { - return await db.query.productGroupInfo.findMany({ - with: { - products: { - with: { - product: true, - }, - }, - }, - }); -} - -export async function createProductGroup(name: string): Promise { - const [group] = await db.insert(productGroupInfo).values({ name }).returning(); - return group; -} - -export async function updateProductGroup(id: number, name: string): Promise { - const [group] = await db.update(productGroupInfo) - .set({ name }) - .where(eq(productGroupInfo.id, id)) - .returning(); - return group; -} - -export async function deleteProductGroup(id: number): Promise { - await db.delete(productGroupInfo).where(eq(productGroupInfo.id, id)); -} - -export async function addProductToGroup(groupId: number, productId: number): Promise { - await db.insert(productGroupMembership).values({ groupId, productId }); -} - -export async function removeProductFromGroup(groupId: number, productId: number): Promise { - await db.delete(productGroupMembership) - .where(and( - eq(productGroupMembership.groupId, groupId), - eq(productGroupMembership.productId, productId) - )); -} diff --git a/packages/db_helper_postgres/src/helper_methods/slots.ts b/packages/db_helper_postgres/src/helper_methods/slots.ts deleted file mode 100644 index 6779bc2..0000000 --- a/packages/db_helper_postgres/src/helper_methods/slots.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { db } from '../db/db_index'; -import { deliverySlotInfo, productInfo, vendorSnippets } from '../db/schema'; -import { eq, and, inArray, desc } from 'drizzle-orm'; - -export async function getAllSlots(): Promise { - const slots = await db.query.deliverySlotInfo.findMany({ - orderBy: desc(deliverySlotInfo.createdAt), - with: { - vendorSnippets: true, - }, - }); - - // Fetch products for all slots - const allProductIds = new Set(); - for (const slot of slots) { - for (const productId of (slot.productIds || [])) { - allProductIds.add(productId); - } - } - - const productIdsArray = Array.from(allProductIds); - const productsData = productIdsArray.length > 0 - ? await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIdsArray), - }) - : []; - - const productMap = new Map(productsData.map(p => [p.id, p])); - - return slots.map(slot => ({ - ...slot, - products: (slot.productIds || []) - .map(productId => productMap.get(productId)) - .filter((p): p is NonNullable => p != null), - })); -} - -export async function getSlotById(id: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, id), - with: { - vendorSnippets: { - with: { - slot: true, - }, - }, - }, - }); - - if (!slot) { - return null; - } - - // Fetch products for this slot - const productIds = slot.productIds || []; - const productsData = productIds.length > 0 - ? await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - }) - : []; - - return { - ...slot, - products: productsData, - }; -} - -export async function createSlot(input: any): Promise { - const [slot] = await db.insert(deliverySlotInfo).values(input).returning(); - return slot; -} - -export async function updateSlot(id: number, updates: any): Promise { - const [slot] = await db.update(deliverySlotInfo) - .set(updates) - .where(eq(deliverySlotInfo.id, id)) - .returning(); - return slot; -} - -export async function deleteSlot(id: number): Promise { - await db.delete(deliverySlotInfo).where(eq(deliverySlotInfo.id, id)); -} - -export async function getSlotProducts(slotId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - return []; - } - - const productIds = slot.productIds || []; - if (productIds.length === 0) { - return []; - } - - return await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - }); -} - -export async function addProductToSlot(slotId: number, productId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - throw new Error(`Slot ${slotId} not found`); - } - - const currentProductIds = slot.productIds || []; - if (!currentProductIds.includes(productId)) { - await db.update(deliverySlotInfo) - .set({ productIds: [...currentProductIds, productId] }) - .where(eq(deliverySlotInfo.id, slotId)); - } -} - -export async function removeProductFromSlot(slotId: number, productId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - throw new Error(`Slot ${slotId} not found`); - } - - const currentProductIds = slot.productIds || []; - const updatedProductIds = currentProductIds.filter(id => id !== productId); - - await db.update(deliverySlotInfo) - .set({ productIds: updatedProductIds }) - .where(eq(deliverySlotInfo.id, slotId)); -} - -export async function clearSlotProducts(slotId: number): Promise { - await db.update(deliverySlotInfo) - .set({ productIds: [] }) - .where(eq(deliverySlotInfo.id, slotId)); -} - -export async function updateSlotCapacity(slotId: number, maxCapacity: number): Promise { - const [slot] = await db.update(deliverySlotInfo) - .set({ maxCapacity }) - .where(eq(deliverySlotInfo.id, slotId)) - .returning(); - return slot; -} - -export async function getSlotDeliverySequence(slotId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - columns: { - deliverySequence: true, - }, - }); - return slot?.deliverySequence || null; -} - -export async function updateSlotDeliverySequence(slotId: number, sequence: any): Promise { - await db.update(deliverySlotInfo) - .set({ deliverySequence: sequence }) - .where(eq(deliverySlotInfo.id, slotId)); -} diff --git a/packages/db_helper_postgres/src/helper_methods/staff-user.ts b/packages/db_helper_postgres/src/helper_methods/staff-user.ts deleted file mode 100644 index f02f8b0..0000000 --- a/packages/db_helper_postgres/src/helper_methods/staff-user.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { db } from '../db/db_index'; -import { staffUsers, staffRoles, users, userDetails, orders } from '../db/schema'; -import { eq, or, ilike, and, lt, desc } from 'drizzle-orm'; - -export interface StaffUser { - id: number; - name: string; - password: string; - staffRoleId: number; - createdAt: Date; -} - -export async function getStaffUserByName(name: string): Promise { - const staff = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - - return staff || null; -} - -export async function getAllStaff(): Promise { - const staff = await db.query.staffUsers.findMany({ - columns: { - id: true, - name: true, - }, - with: { - role: { - with: { - rolePermissions: { - with: { - permission: true, - }, - }, - }, - }, - }, - }); - - return staff; -} - -export async function getStaffByName(name: string): Promise { - const staff = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - return staff || null; -} - -export async function getAllUsers( - cursor?: number, - limit: number = 20, - search?: string -): Promise<{ users: any[]; hasMore: boolean }> { - let whereCondition = undefined; - - if (search) { - whereCondition = or( - ilike(users.name, `%${search}%`), - ilike(users.email, `%${search}%`), - ilike(users.mobile, `%${search}%`) - ); - } - - if (cursor) { - const cursorCondition = lt(users.id, cursor); - whereCondition = whereCondition ? and(whereCondition, cursorCondition) : cursorCondition; - } - - const allUsers = await db.query.users.findMany({ - where: whereCondition, - with: { - userDetails: true, - }, - orderBy: desc(users.id), - limit: limit + 1, - }); - - const hasMore = allUsers.length > limit; - const usersToReturn = hasMore ? allUsers.slice(0, limit) : allUsers; - - return { users: usersToReturn, hasMore }; -} - -export async function getUserWithDetails(userId: number): Promise { - const user = await db.query.users.findFirst({ - where: eq(users.id, userId), - with: { - userDetails: true, - orders: { - orderBy: desc(orders.createdAt), - limit: 1, - }, - }, - }); - - return user || null; -} - -export async function updateUserSuspension(userId: number, isSuspended: boolean): Promise { - await db - .insert(userDetails) - .values({ userId, isSuspended }) - .onConflictDoUpdate({ - target: userDetails.userId, - set: { isSuspended }, - }); -} - -export async function checkStaffUserExists(name: string): Promise { - const existingUser = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - return !!existingUser; -} - -export async function checkStaffRoleExists(roleId: number): Promise { - const role = await db.query.staffRoles.findFirst({ - where: eq(staffRoles.id, roleId), - }); - return !!role; -} - -export async function createStaffUser( - name: string, - password: string, - roleId: number -): Promise { - const [newUser] = await db.insert(staffUsers).values({ - name: name.trim(), - password, - staffRoleId: roleId, - }).returning(); - - return { - id: newUser.id, - name: newUser.name, - password: newUser.password, - staffRoleId: newUser.staffRoleId, - createdAt: newUser.createdAt, - }; -} - -export async function getAllRoles(): Promise { - const roles = await db.query.staffRoles.findMany({ - columns: { - id: true, - roleName: true, - }, - }); - - return roles; -} diff --git a/packages/db_helper_postgres/src/helper_methods/store.ts b/packages/db_helper_postgres/src/helper_methods/store.ts deleted file mode 100644 index cb1d755..0000000 --- a/packages/db_helper_postgres/src/helper_methods/store.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { db } from '../db/db_index'; -import { storeInfo, productInfo } from '../db/schema'; -import { eq, inArray } from 'drizzle-orm'; - -export interface Store { - id: number; - name: string; - description: string | null; - imageUrl: string | null; - owner: number; - createdAt: Date; - updatedAt: Date; -} - -export async function getAllStores(): Promise { - const stores = await db.query.storeInfo.findMany({ - with: { - owner: true, - }, - }); - - return stores; -} - -export async function getStoreById(id: number): Promise { - const store = await db.query.storeInfo.findFirst({ - where: eq(storeInfo.id, id), - with: { - owner: true, - }, - }); - - return store || null; -} - -export interface CreateStoreInput { - name: string; - description?: string; - imageUrl?: string; - owner: number; -} - -export async function createStore( - input: CreateStoreInput, - products?: number[] -): Promise { - const [newStore] = await db - .insert(storeInfo) - .values({ - name: input.name, - description: input.description, - imageUrl: input.imageUrl, - owner: input.owner, - }) - .returning(); - - // Assign selected products to this store - if (products && products.length > 0) { - await db - .update(productInfo) - .set({ storeId: newStore.id }) - .where(inArray(productInfo.id, products)); - } - - return { - id: newStore.id, - name: newStore.name, - description: newStore.description, - imageUrl: newStore.imageUrl, - owner: newStore.owner, - createdAt: newStore.createdAt, - updatedAt: newStore.updatedAt, - }; -} - -export interface UpdateStoreInput { - name?: string; - description?: string; - imageUrl?: string; - owner?: number; -} - -export async function updateStore( - id: number, - input: UpdateStoreInput, - products?: number[] -): Promise { - const [updatedStore] = await db - .update(storeInfo) - .set({ - ...input, - updatedAt: new Date(), - }) - .where(eq(storeInfo.id, id)) - .returning(); - - if (!updatedStore) { - throw new Error("Store not found"); - } - - // Update products if provided - if (products !== undefined) { - // First, set storeId to null for products not in the list but currently assigned to this store - await db - .update(productInfo) - .set({ storeId: null }) - .where(eq(productInfo.storeId, id)); - - // Then, assign the selected products to this store - if (products.length > 0) { - await db - .update(productInfo) - .set({ storeId: id }) - .where(inArray(productInfo.id, products)); - } - } - - return { - id: updatedStore.id, - name: updatedStore.name, - description: updatedStore.description, - imageUrl: updatedStore.imageUrl, - owner: updatedStore.owner, - createdAt: updatedStore.createdAt, - updatedAt: updatedStore.updatedAt, - }; -} - -export async function deleteStore(id: number): Promise<{ message: string }> { - return await db.transaction(async (tx) => { - // First, update all products of this store to set storeId to null - await tx - .update(productInfo) - .set({ storeId: null }) - .where(eq(productInfo.storeId, id)); - - // Then delete the store - const [deletedStore] = await tx - .delete(storeInfo) - .where(eq(storeInfo.id, id)) - .returning(); - - if (!deletedStore) { - throw new Error("Store not found"); - } - - return { - message: "Store deleted successfully", - }; - }); -} diff --git a/packages/db_helper_postgres/src/helper_methods/user.ts b/packages/db_helper_postgres/src/helper_methods/user.ts deleted file mode 100644 index e25fcac..0000000 --- a/packages/db_helper_postgres/src/helper_methods/user.ts +++ /dev/null @@ -1,270 +0,0 @@ -import { db } from '../db/db_index'; -import { users, userDetails, orders, orderItems, complaints, notifCreds, unloggedUserTokens, userIncidents, orderStatus } from '../db/schema'; -import { eq, sql, desc, asc, count, max, inArray } from 'drizzle-orm'; - -export async function createUserByMobile(mobile: string): Promise { - const [newUser] = await db - .insert(users) - .values({ - name: null, - email: null, - mobile, - }) - .returning(); - - return newUser; -} - -export async function getUserByMobile(mobile: string): Promise { - const [existingUser] = await db - .select() - .from(users) - .where(eq(users.mobile, mobile)) - .limit(1); - - return existingUser || null; -} - -export async function getUnresolvedComplaintsCount(): Promise { - const result = await db - .select({ count: count(complaints.id) }) - .from(complaints) - .where(eq(complaints.isResolved, false)); - - return result[0]?.count || 0; -} - -export async function getAllUsersWithFilters( - limit: number, - cursor?: number, - search?: string -): Promise<{ users: any[]; hasMore: boolean }> { - const whereConditions = []; - - if (search && search.trim()) { - whereConditions.push(sql`${users.mobile} ILIKE ${`%${search.trim()}%`}`); - } - - if (cursor) { - whereConditions.push(sql`${users.id} > ${cursor}`); - } - - const usersList = await db - .select({ - id: users.id, - name: users.name, - mobile: users.mobile, - createdAt: users.createdAt, - }) - .from(users) - .where(whereConditions.length > 0 ? sql.join(whereConditions, sql` AND `) : undefined) - .orderBy(asc(users.id)) - .limit(limit + 1); - - const hasMore = usersList.length > limit; - const usersToReturn = hasMore ? usersList.slice(0, limit) : usersList; - - return { users: usersToReturn, hasMore }; -} - -export async function getOrderCountsByUserIds(userIds: number[]): Promise<{ userId: number; totalOrders: number }[]> { - if (userIds.length === 0) return []; - - return await db - .select({ - userId: orders.userId, - totalOrders: count(orders.id), - }) - .from(orders) - .where(sql`${orders.userId} IN (${sql.join(userIds, sql`, `)})`) - .groupBy(orders.userId); -} - -export async function getLastOrdersByUserIds(userIds: number[]): Promise<{ userId: number; lastOrderDate: Date | null }[]> { - if (userIds.length === 0) return []; - - return await db - .select({ - userId: orders.userId, - lastOrderDate: max(orders.createdAt), - }) - .from(orders) - .where(sql`${orders.userId} IN (${sql.join(userIds, sql`, `)})`) - .groupBy(orders.userId); -} - -export async function getSuspensionStatusesByUserIds(userIds: number[]): Promise<{ userId: number; isSuspended: boolean }[]> { - if (userIds.length === 0) return []; - - return await db - .select({ - userId: userDetails.userId, - isSuspended: userDetails.isSuspended, - }) - .from(userDetails) - .where(sql`${userDetails.userId} IN (${sql.join(userIds, sql`, `)})`); -} - -export async function getUserBasicInfo(userId: number): Promise { - const user = await db - .select({ - id: users.id, - name: users.name, - mobile: users.mobile, - createdAt: users.createdAt, - }) - .from(users) - .where(eq(users.id, userId)) - .limit(1); - - return user[0] || null; -} - -export async function getUserSuspensionStatus(userId: number): Promise { - const userDetail = await db - .select({ - isSuspended: userDetails.isSuspended, - }) - .from(userDetails) - .where(eq(userDetails.userId, userId)) - .limit(1); - - return userDetail[0]?.isSuspended ?? false; -} - -export async function getUserOrders(userId: number): Promise { - return await db - .select({ - id: orders.id, - readableId: orders.readableId, - totalAmount: orders.totalAmount, - createdAt: orders.createdAt, - isFlashDelivery: orders.isFlashDelivery, - }) - .from(orders) - .where(eq(orders.userId, userId)) - .orderBy(desc(orders.createdAt)); -} - -export async function getOrderStatusesByOrderIds(orderIds: number[]): Promise<{ orderId: number; isDelivered: boolean; isCancelled: boolean }[]> { - if (orderIds.length === 0) return []; - - return await db - .select({ - orderId: orderStatus.orderId, - isDelivered: orderStatus.isDelivered, - isCancelled: orderStatus.isCancelled, - }) - .from(orderStatus) - .where(sql`${orderStatus.orderId} IN (${sql.join(orderIds, sql`, `)})`); -} - -export async function getItemCountsByOrderIds(orderIds: number[]): Promise<{ orderId: number; itemCount: number }[]> { - if (orderIds.length === 0) return []; - - return await db - .select({ - orderId: orderItems.orderId, - itemCount: count(orderItems.id), - }) - .from(orderItems) - .where(sql`${orderItems.orderId} IN (${sql.join(orderIds, sql`, `)})`) - .groupBy(orderItems.orderId); -} - -export async function upsertUserSuspension(userId: number, isSuspended: boolean): Promise { - const existingDetail = await db - .select({ id: userDetails.id }) - .from(userDetails) - .where(eq(userDetails.userId, userId)) - .limit(1); - - if (existingDetail.length > 0) { - await db - .update(userDetails) - .set({ isSuspended }) - .where(eq(userDetails.userId, userId)); - } else { - await db - .insert(userDetails) - .values({ - userId, - isSuspended, - }); - } -} - -export async function searchUsers(search?: string): Promise { - if (search && search.trim()) { - return await db - .select({ - id: users.id, - name: users.name, - mobile: users.mobile, - }) - .from(users) - .where(sql`${users.mobile} ILIKE ${`%${search.trim()}%`} OR ${users.name} ILIKE ${`%${search.trim()}%`}`); - } else { - return await db - .select({ - id: users.id, - name: users.name, - mobile: users.mobile, - }) - .from(users); - } -} - -export async function getAllNotifCreds(): Promise<{ userId: number }[]> { - return await db - .select({ userId: notifCreds.userId }) - .from(notifCreds); -} - -export async function getAllUnloggedTokens(): Promise<{ token: string }[]> { - return await db - .select({ token: unloggedUserTokens.token }) - .from(unloggedUserTokens); -} - -export async function getNotifTokensByUserIds(userIds: number[]): Promise<{ token: string }[]> { - return await db - .select({ token: notifCreds.token }) - .from(notifCreds) - .where(inArray(notifCreds.userId, userIds)); -} - -export async function getUserIncidentsWithRelations(userId: number): Promise { - return await db.query.userIncidents.findMany({ - where: eq(userIncidents.userId, userId), - with: { - order: { - with: { - orderStatus: true, - }, - }, - addedBy: true, - }, - orderBy: desc(userIncidents.dateAdded), - }); -} - -export async function createUserIncident( - userId: number, - orderId: number | undefined, - adminComment: string | undefined, - adminUserId: number, - negativityScore: number | undefined -): Promise { - const [incident] = await db.insert(userIncidents) - .values({ - userId, - orderId, - adminComment, - addedBy: adminUserId, - negativityScore, - }) - .returning(); - - return incident; -} diff --git a/packages/db_helper_postgres/src/helper_methods/vendor-snippets.ts b/packages/db_helper_postgres/src/helper_methods/vendor-snippets.ts deleted file mode 100644 index 8cec6a6..0000000 --- a/packages/db_helper_postgres/src/helper_methods/vendor-snippets.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { db } from '../db/db_index'; -import { vendorSnippets, deliverySlotInfo, productInfo, orders, orderItems, orderStatus } from '../db/schema'; -import { eq, and, inArray, gt, sql, asc } from 'drizzle-orm'; - -export async function checkVendorSnippetExists(snippetCode: string): Promise { - const existingSnippet = await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.snippetCode, snippetCode), - }); - return !!existingSnippet; -} - -export async function getVendorSnippetById(id: number): Promise { - return await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.id, id), - with: { - slot: true, - }, - }); -} - -export async function getVendorSnippetByCode(snippetCode: string): Promise { - return await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.snippetCode, snippetCode), - }); -} - -export async function getAllVendorSnippets(): Promise { - return await db.query.vendorSnippets.findMany({ - with: { - slot: true, - }, - orderBy: (vendorSnippets, { desc }) => [desc(vendorSnippets.createdAt)], - }); -} - -export interface CreateVendorSnippetInput { - snippetCode: string; - slotId?: number; - productIds: number[]; - isPermanent: boolean; - validTill?: Date; -} - -export async function createVendorSnippet(input: CreateVendorSnippetInput): Promise { - const [result] = await db.insert(vendorSnippets).values({ - snippetCode: input.snippetCode, - slotId: input.slotId, - productIds: input.productIds, - isPermanent: input.isPermanent, - validTill: input.validTill, - }).returning(); - - return result; -} - -export async function updateVendorSnippet(id: number, updates: any): Promise { - const [result] = await db.update(vendorSnippets) - .set(updates) - .where(eq(vendorSnippets.id, id)) - .returning(); - - return result; -} - -export async function deleteVendorSnippet(id: number): Promise { - await db.delete(vendorSnippets) - .where(eq(vendorSnippets.id, id)); -} - -export async function getProductsByIds(productIds: number[]): Promise { - return await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - columns: { id: true, name: true }, - }); -} - -export async function getVendorSlotById(slotId: number): Promise { - return await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); -} - -export async function getVendorOrdersBySlotId(slotId: number): Promise { - return await db.query.orders.findMany({ - where: eq(orders.slotId, slotId), - with: { - orderItems: { - with: { - product: { - with: { - unit: true, - }, - }, - }, - }, - orderStatus: true, - user: true, - slot: true, - }, - orderBy: (orders, { desc }) => [desc(orders.createdAt)], - }); -} - -export async function getOrderItemsByOrderIds(orderIds: number[]): Promise { - return await db.query.orderItems.findMany({ - where: inArray(orderItems.orderId, orderIds), - with: { - product: { - with: { - unit: true, - }, - }, - }, - }); -} - -export async function getOrderStatusByOrderIds(orderIds: number[]): Promise { - return await db.query.orderStatus.findMany({ - where: inArray(orderStatus.orderId, orderIds), - }); -} - -export async function updateVendorOrderItemPackaging(orderItemId: number, isPackaged: boolean, isPackageVerified: boolean): Promise { - await db.update(orderItems) - .set({ - is_packaged: isPackaged, - is_package_verified: isPackageVerified, - }) - .where(eq(orderItems.id, orderItemId)); -} diff --git a/packages/db_helper_postgres/src/lib/automated-jobs.ts b/packages/db_helper_postgres/src/lib/automated-jobs.ts index 75f5723..756ab1f 100644 --- a/packages/db_helper_postgres/src/lib/automated-jobs.ts +++ b/packages/db_helper_postgres/src/lib/automated-jobs.ts @@ -1,21 +1,6 @@ import { db } from '../db/db_index' -import { productInfo, keyValStore } from '../db/schema' -import { inArray, eq } from 'drizzle-orm' - -/** - * Toggle flash delivery availability for specific products - * @param isAvailable - Whether flash delivery should be available - * @param productIds - Array of product IDs to update - */ -export async function toggleFlashDeliveryForItems( - isAvailable: boolean, - productIds: number[] -): Promise { - await db - .update(productInfo) - .set({ isFlashAvailable: isAvailable }) - .where(inArray(productInfo.id, productIds)) -} +import { keyValStore } from '../db/schema' +import { eq } from 'drizzle-orm' /** * Update key-value store diff --git a/packages/db_helper_postgres/src/user-apis/order.ts b/packages/db_helper_postgres/src/user-apis/order.ts index eb9d8e6..9c094de 100644 --- a/packages/db_helper_postgres/src/user-apis/order.ts +++ b/packages/db_helper_postgres/src/user-apis/order.ts @@ -20,32 +20,6 @@ import type { UserOrderDetail, } from '@packages/shared' -export interface OrderItemInput { - productId: number - quantity: number - slotId: number | null -} - -export interface PlaceOrderInput { - userId: number - selectedItems: OrderItemInput[] - addressId: number - paymentMethod: 'online' | 'cod' - couponId?: number - userNotes?: string - isFlash?: boolean -} - -export interface OrderGroupData { - slotId: number | null - items: Array<{ - productId: number - quantity: number - slotId: number | null - product: typeof productInfo.$inferSelect - }> -} - export interface PlacedOrder { id: number userId: number diff --git a/packages/db_helper_postgres/src/user-apis/product.ts b/packages/db_helper_postgres/src/user-apis/product.ts index 42ebcc8..6be8a5e 100644 --- a/packages/db_helper_postgres/src/user-apis/product.ts +++ b/packages/db_helper_postgres/src/user-apis/product.ts @@ -215,7 +215,7 @@ export async function getAllProductsWithUnits(tagId?: number): Promise { +export async function getSuspendedSkuIds(): Promise { const suspendedProducts = await db .select({ id: productInfo.id }) .from(productInfo) diff --git a/packages/db_helper_postgres/src/user-apis/tags.ts b/packages/db_helper_postgres/src/user-apis/tags.ts deleted file mode 100644 index 49524b2..0000000 --- a/packages/db_helper_postgres/src/user-apis/tags.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { db } from '../db/db_index'; -import { productTags } from '../db/schema'; -import { eq } from 'drizzle-orm'; - -export async function getAllTags(): Promise { - return await db.query.productTags.findMany({ - with: { - // products: { - // with: { - // product: true, - // }, - // }, - }, - }); -} - -export async function getTagById(id: number): Promise { - return await db.query.productTags.findFirst({ - where: eq(productTags.id, id), - with: { - // products: { - // with: { - // product: true, - // }, - // }, - }, - }); -} diff --git a/packages/db_helper_sqlite/index.ts b/packages/db_helper_sqlite/index.ts index ea3700c..29af47c 100644 --- a/packages/db_helper_sqlite/index.ts +++ b/packages/db_helper_sqlite/index.ts @@ -6,9 +6,6 @@ export { db, initDb } from './src/db/db_index' // Re-export schema export * from './src/db/schema' -// Export enum types for type safety -export { staffRoleEnum, staffPermissionEnum } from './src/db/schema' - // Admin API helpers - explicitly namespaced exports to avoid duplicates export { // Banner @@ -83,14 +80,10 @@ export { checkProductExistsByName, checkUnitExists, getProductImagesById, - createSpecialDealsForSku, - updateSkuDeals, replaceProductTags, replaceTagProducts, - mergeSkus, updateSlotProducts, getSlotsProductIds, - getAllUnits, getAllProductTags, getAllProductTagInfos, getProductTagInfoById, @@ -105,8 +98,6 @@ export { createProductGroup, updateProductGroup, deleteProductGroup, - addProductToGroup, - removeProductFromGroup, updateProductPrices, } from './src/admin-apis/product' @@ -137,7 +128,6 @@ export { getAllStaff, getAllUsers, getUserWithDetails, - updateUserSuspensionStatus, checkStaffUserExists, checkStaffRoleExists, createStaffUser, @@ -188,8 +178,6 @@ export { getProductsByIds, getVendorSlotById, getVendorOrdersBySlotId, - getOrderItemsByOrderIds, - getOrderStatusByOrderIds, updateVendorOrderItemPackaging, getVendorOrders, } from './src/admin-apis/vendor-snippets' @@ -233,9 +221,6 @@ export { getAllSkusSummary, getOffersAndCombos, type ProductSummaryData, - type SkuSummary, - type OffersPageData, - type OffersPageProductData, } from './src/user-apis/product' export { @@ -285,7 +270,6 @@ export { getUserById as getUserProfileById, getUserDetailByUserId as getUserProfileDetailById, getUserWithCreds as getUserWithCreds, - getNotifCred as getUserNotifCred, upsertNotifCred as upsertUserNotifCred, deleteUnloggedToken as deleteUserUnloggedToken, getUnloggedToken as getUserUnloggedToken, @@ -313,8 +297,6 @@ export { // Post-order handler helpers getOrdersByIdsWithFullData, getOrderByIdWithFullData, - type OrderWithFullData, - type OrderWithCancellationData, } from './src/user-apis/order' // Store Helpers (for cache initialization) @@ -331,7 +313,6 @@ export { getAllProductTagsForCache, getAllProductCombosForCache, type ProductBasicData, - type AvailabilityCacheData, type StoreBasicData, type DeliverySlotData, type SpecialDealData, @@ -379,11 +360,8 @@ export { // SKU Features Helper export { - cleanFeatureValue, - splitQuantityFeature, composeUnitNotation, composeSkuName, - type SkuFeatureLike, } from './src/lib/sku-features' // Upload URL Helpers diff --git a/packages/db_helper_sqlite/src/admin-apis/product.ts b/packages/db_helper_sqlite/src/admin-apis/product.ts index 0b7ff88..96a0452 100644 --- a/packages/db_helper_sqlite/src/admin-apis/product.ts +++ b/packages/db_helper_sqlite/src/admin-apis/product.ts @@ -100,7 +100,6 @@ interface CreateProductInput { productType?: 'item' | 'combo' skus: CreateSkuInput[] } -type UnitRow = InferSelectModel type StoreRow = InferSelectModel type SpecialDealRow = InferSelectModel type ProductTagInfoRow = InferSelectModel @@ -114,12 +113,6 @@ const getStringArray = (value: unknown): string[] | null => { return value.map((item) => String(item)) } -const mapUnit = (unit: UnitRow): AdminUnit => ({ - id: unit.id, - shortNotation: unit.shortNotation, - fullName: unit.fullName, -}) - const mapStore = (store: StoreRow): Store => ({ id: store.id, name: store.name, @@ -647,14 +640,6 @@ export async function updateSlotProducts(slotId: string, productIds: string[]): } } -export async function getAllUnits(): Promise { - const allUnits = await db.query.units.findMany({ - orderBy: units.shortNotation, - }) - - return allUnits.map(mapUnit) -} - export async function getAllProductTags(): Promise { const tags = await db.query.productTagInfo.findMany({ with: { @@ -1029,18 +1014,6 @@ export async function deleteProductGroup(id: number): Promise { - await db.insert(productGroupMembership).values({ groupId, productId }) -} - -export async function removeProductFromGroup(groupId: number, productId: number): Promise { - await db.delete(productGroupMembership) - .where(and( - eq(productGroupMembership.groupId, groupId), - eq(productGroupMembership.productId, productId) - )) -} - export async function updateProductPrices(updates: Array<{ productId: number price?: number @@ -1142,101 +1115,6 @@ export async function getProductImagesById(productId: number): Promise { - if (deals.length === 0) { - return [] - } - - const dealInserts = deals.map((deal) => ({ - skuId, - quantity: deal.quantity.toString(), - price: deal.price.toString(), - validTill: new Date(deal.validTill), - })) - - const createdDeals = await db - .insert(specialDeals) - .values(dealInserts) - .returning() - - return createdDeals.map(mapSpecialDeal) -} - -export async function updateSkuDeals( - productId: number, - deals: CreateSpecialDealInput[] -): Promise { - if (deals.length === 0) { - await db.delete(specialDeals).where(eq(specialDeals.skuId, productId)) - return - } - - const existingDeals = await db.query.specialDeals.findMany({ - where: eq(specialDeals.skuId, productId), - }) - - const existingDealsMap = new Map( - existingDeals.map((deal: SpecialDealRow) => [`${deal.quantity}-${deal.price}`, deal]) - ) - const newDealsMap = new Map( - deals.map((deal) => [`${deal.quantity}-${deal.price}`, deal]) - ) - - const dealsToAdd = deals.filter((deal) => { - const key = `${deal.quantity}-${deal.price}` - return !existingDealsMap.has(key) - }) - - const dealsToRemove = existingDeals.filter((deal: SpecialDealRow) => { - const key = `${deal.quantity}-${deal.price}` - return !newDealsMap.has(key) - }) - - const dealsToUpdate = deals.filter((deal: CreateSpecialDealInput) => { - const key = `${deal.quantity}-${deal.price}` - const existing = existingDealsMap.get(key) - const nextValidTill = deal.validTill instanceof Date - ? deal.validTill.toISOString().split('T')[0] - : String(deal.validTill) - return existing && existing.validTill.toISOString().split('T')[0] !== nextValidTill - }) - - if (dealsToRemove.length > 0) { - await db.delete(specialDeals).where( - inArray(specialDeals.id, dealsToRemove.map((deal: SpecialDealRow) => deal.id)) - ) - } - - if (dealsToAdd.length > 0) { - const dealInserts = dealsToAdd.map((deal) => ({ - skuId: productId, - quantity: deal.quantity.toString(), - price: deal.price.toString(), - validTill: new Date(deal.validTill), - })) - await db.insert(specialDeals).values(dealInserts) - } - - for (const deal of dealsToUpdate) { - const key = `${deal.quantity}-${deal.price}` - const existingDeal = existingDealsMap.get(key) - if (existingDeal) { - await db.update(specialDeals) - .set({ validTill: new Date(deal.validTill) }) - .where(eq(specialDeals.id, existingDeal.id)) - } - } -} - export async function replaceProductTags(productId: number, tagIds: number[]): Promise { await db.delete(productTags).where(eq(productTags.productId, productId)) @@ -1266,128 +1144,3 @@ export async function replaceTagProducts(tagId: number, productIds: number[]): P await db.insert(productTags).values(productAssociations) } - -export async function mergeSkus(fromSkuId: number, toSkuId: number) { - if (fromSkuId === toSkuId) { - return { fromSkuId, toSkuId, message: 'SKUs are the same, no merge needed', counts: {} } - } - - const fromSku = await db.query.productSkus.findFirst({ where: eq(productSkus.id, fromSkuId) }) - if (!fromSku) throw new Error(`SKU ${fromSkuId} not found`) - - const toSku = await db.query.productSkus.findFirst({ where: eq(productSkus.id, toSkuId) }) - if (!toSku) throw new Error(`SKU ${toSkuId} not found`) - - const counts: Record = {} - - // 1. order_items โ€” direct update - const orderItemsResult = await db.update(orderItems) - .set({ skuId: toSkuId }) - .where(eq(orderItems.skuId, fromSkuId)) - counts.orderItems = orderItemsResult.changes ?? 0 - - // 2. special_deals โ€” direct update - const specialDealsResult = await db.update(specialDeals) - .set({ skuId: toSkuId }) - .where(eq(specialDeals.skuId, fromSkuId)) - counts.specialDeals = specialDealsResult.changes ?? 0 - - // 3. cart_items โ€” delete all with fromSkuId - const cartResult = await db.delete(cartItems) - .where(eq(cartItems.skuId, fromSkuId)) - counts.cartItems = cartResult.changes ?? 0 - - // 4. coupon_applicable_products โ€” update, but handle unique constraint - // First delete rows where (coupon_id, toSkuId) already exists - const existingCapRows = await db.query.couponApplicableProducts.findMany({ - where: eq(couponApplicableProducts.skuId, toSkuId), - columns: { couponId: true }, - }) - const existingCouponIds = new Set(existingCapRows.map((r) => r.couponId)) - - if (existingCouponIds.size > 0) { - const dupResult = await db.delete(couponApplicableProducts) - .where( - and( - eq(couponApplicableProducts.skuId, fromSkuId), - inArray(couponApplicableProducts.couponId, Array.from(existingCouponIds)) - ) - ) - counts.couponDedupDeleted = dupResult.changes ?? 0 - } - - // Now update remaining rows - const capResult = await db.update(couponApplicableProducts) - .set({ skuId: toSkuId }) - .where(eq(couponApplicableProducts.skuId, fromSkuId)) - counts.couponApplicable = capResult.changes ?? 0 - - // 5. JSON arrays โ€” remap fromSkuId to toSkuId - const jsonTables: Array<{ table: any; column: string; name: string }> = [ - { table: deliverySlotInfo, column: 'skuIds', name: 'deliverySlotInfo' }, - { table: homeBanners, column: 'skuIds', name: 'homeBanners' }, - { table: coupons, column: 'skuIds', name: 'coupons' }, - { table: reservedCoupons, column: 'skuIds', name: 'reservedCoupons' }, - { table: vendorSnippets, column: 'skuIds', name: 'vendorSnippets' }, - ] - - for (const { table, column, name } of jsonTables) { - const rows = await db.select({ id: table.id, ids: table[column] }).from(table) - let updated = 0 - for (const row of rows) { - const ids: number[] = (row.ids as number[]) || [] - if (!ids.includes(fromSkuId)) continue - const newIds = ids.map((id) => (id === fromSkuId ? toSkuId : id)) - const deduped = [...new Set(newIds)] - if (deduped.length !== ids.length || deduped.some((id, i) => id !== ids[i])) { - await db.update(table).set({ [column]: deduped } as any).where(eq(table.id, row.id)) - updated++ - } - } - counts[name] = updated - } - - // popularItems in key_val_store - const kvRow = await db.query.keyValStore.findFirst({ - where: eq(keyValStore.key, 'popularItems'), - }) - if (kvRow && kvRow.value) { - try { - const arr: number[] = JSON.parse(kvRow.value) - if (arr.includes(fromSkuId)) { - const newArr = [...new Set(arr.map((id) => (id === fromSkuId ? toSkuId : id)))] - await db.update(keyValStore) - .set({ value: JSON.stringify(newArr) }) - .where(eq(keyValStore.key, 'popularItems')) - counts.popularItems = 1 - } - } catch { /* value not valid JSON, skip */ } - } - - // 6. Delete SKU features and the SKU itself - const featuresResult = await db.delete(skuFeatures).where(eq(skuFeatures.skuId, fromSkuId)) - counts.skuFeatures = featuresResult.changes ?? 0 - - const skuResult = await db.delete(productSkus).where(eq(productSkus.id, fromSkuId)) - counts.productSkus = skuResult.changes ?? 0 - - // 7. Delete orphaned product - const remainingSkus = await db.query.productSkus.findMany({ - where: eq(productSkus.productId, fromSku.productId), - columns: { id: true }, - }) - - let orphanedProductId: number | undefined - if (remainingSkus.length === 0) { - await db.delete(productInfo).where(eq(productInfo.id, fromSku.productId)) - orphanedProductId = fromSku.productId - counts.orphanedProduct = 1 - } - - return { - fromSkuId, - toSkuId, - orphanedProductId, - counts, - } -} diff --git a/packages/db_helper_sqlite/src/admin-apis/staff-user.ts b/packages/db_helper_sqlite/src/admin-apis/staff-user.ts index 64005da..6f18e3e 100644 --- a/packages/db_helper_sqlite/src/admin-apis/staff-user.ts +++ b/packages/db_helper_sqlite/src/admin-apis/staff-user.ts @@ -98,16 +98,6 @@ export async function getUserWithDetails(userId: number): Promise { return user || null } -export async function updateUserSuspensionStatus(userId: number, isSuspended: boolean): Promise { - await db - .insert(userDetails) - .values({ userId, isSuspended }) - .onConflictDoUpdate({ - target: userDetails.userId, - set: { isSuspended }, - }) -} - export async function checkStaffUserExists(name: string): Promise { const existingUser = await db.query.staffUsers.findFirst({ where: eq(staffUsers.name, name), diff --git a/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts b/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts index 3a4c76a..036390e 100644 --- a/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts +++ b/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts @@ -193,26 +193,6 @@ export async function getVendorOrders() { }) } -export async function getOrderItemsByOrderIds(orderIds: number[]) { - return await db.query.orderItems.findMany({ - where: inArray(orderItems.orderId, orderIds), - with: { - sku: { - with: { - product: true, - features: true, - }, - }, - }, - }) -} - -export async function getOrderStatusByOrderIds(orderIds: number[]) { - return await db.query.orderStatus.findMany({ - where: inArray(orderStatus.orderId, orderIds), - }) -} - export async function updateVendorOrderItemPackaging( orderItemId: number, isPackaged: boolean diff --git a/packages/db_helper_sqlite/src/common-apis/utils.ts b/packages/db_helper_sqlite/src/common-apis/utils.ts deleted file mode 100644 index c733a7f..0000000 --- a/packages/db_helper_sqlite/src/common-apis/utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Common utility functions that can be used by both admin and user APIs - -export function formatDate(date: Date): string { - return date.toISOString() -} - -export function generateCode(prefix: string, length: number = 6): string { - const timestamp = Date.now().toString().slice(-length) - const random = Math.random().toString(36).substring(2, 8).toUpperCase() - return `${prefix}${timestamp}${random}` -} - -export function calculateDiscount(amount: number, percent: number, maxDiscount?: number): number { - let discount = (amount * percent) / 100 - if (maxDiscount && discount > maxDiscount) { - discount = maxDiscount - } - return discount -} diff --git a/packages/db_helper_sqlite/src/db/schema.ts b/packages/db_helper_sqlite/src/db/schema.ts index c2247b3..5eeed24 100644 --- a/packages/db_helper_sqlite/src/db/schema.ts +++ b/packages/db_helper_sqlite/src/db/schema.ts @@ -103,19 +103,6 @@ export const userCreds = sqliteTable('user_creds', { createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), }) -export const addressZones = sqliteTable('address_zones', { - id: integer().primaryKey({ autoIncrement: true }), - zoneName: text('zone_name').notNull(), - addedAt: timestampText('added_at').notNull().default(sql`CURRENT_TIMESTAMP`), -}) - -export const addressAreas = sqliteTable('address_areas', { - id: integer().primaryKey({ autoIncrement: true }), - placeName: text('place_name').notNull(), - zoneId: integer('zone_id').references(() => addressZones.id), - createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), -}) - export const addresses = sqliteTable('addresses', { id: integer().primaryKey({ autoIncrement: true }), userId: integer('user_id').notNull().references(() => users.id), @@ -132,7 +119,7 @@ export const addresses = sqliteTable('addresses', { googleMapsUrl: text('google_maps_url'), adminLatitude: real('admin_latitude'), adminLongitude: real('admin_longitude'), - zoneId: integer('zone_id').references(() => addressZones.id), + zoneId: integer('zone_id'), createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), }) @@ -428,12 +415,6 @@ export const notifications = sqliteTable('notifications', { createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), }) -export const productCategories = sqliteTable('product_categories', { - id: integer().primaryKey({ autoIncrement: true }), - name: text().notNull(), - description: text(), -}) - export const cartItems = sqliteTable('cart_items', { id: integer().primaryKey({ autoIncrement: true }), userId: integer('user_id').notNull().references(() => users.id), @@ -542,15 +523,6 @@ export const unloggedUserTokens = sqliteTable('unlogged_user_tokens', { lastVerified: timestampText('last_verified'), }) -export const userNotifications = sqliteTable('user_notifications', { - id: integer().primaryKey({ autoIncrement: true }), - title: text('title').notNull(), - imageUrl: text('image_url'), - createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), - body: text('body').notNull(), - applicableUsers: jsonText('applicable_users'), -}) - // Relations export const usersRelations = relations(users, ({ many, one }) => ({ addresses: many(addresses), @@ -579,7 +551,6 @@ export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({ export const addressesRelations = relations(addresses, ({ one, many }) => ({ user: one(users, { fields: [addresses.userId], references: [users.id] }), orders: many(orders), - zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), })) export const unitsRelations = relations(units, ({}) => ({ @@ -683,8 +654,6 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ user: one(users, { fields: [notifications.userId], references: [users.id] }), })) -export const productCategoriesRelations = relations(productCategories, ({}) => ({})) - export const cartItemsRelations = relations(cartItems, ({ one }) => ({ user: one(users, { fields: [cartItems.userId], references: [users.id] }), sku: one(productSkus, { fields: [cartItems.skuId], references: [productSkus.id] }), @@ -717,10 +686,6 @@ export const notifCredsRelations = relations(notifCreds, ({ one }) => ({ user: one(users, { fields: [notifCreds.userId], references: [users.id] }), })) -export const userNotificationsRelations = relations(userNotifications, ({}) => ({ - // No relations needed for now -})) - export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({ owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }), products: many(productInfo), @@ -746,15 +711,6 @@ export const productReviewsRelations = relations(productReviews, ({ one }) => ({ product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }), })) -export const addressZonesRelations = relations(addressZones, ({ many }) => ({ - addresses: many(addresses), - areas: many(addressAreas), -})) - -export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ - zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), -})) - export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({ memberships: many(productGroupMembership), })) diff --git a/packages/db_helper_sqlite/src/db/types.ts b/packages/db_helper_sqlite/src/db/types.ts deleted file mode 100644 index 84e2c17..0000000 --- a/packages/db_helper_sqlite/src/db/types.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { InferSelectModel } from 'drizzle-orm' -import type { - users, - addresses, - units, - productInfo, - productSkus, - skuFeatures, - deliverySlotInfo, - specialDeals, - orders, - orderItems, - payments, - notifications, - productCategories, - cartItems, - coupons, -} from '@/src/db/schema' - -export type User = InferSelectModel -export type Address = InferSelectModel -export type Unit = InferSelectModel -export type ProductInfo = InferSelectModel -export type ProductSku = InferSelectModel -export type SkuFeature = InferSelectModel -export type DeliverySlotInfo = InferSelectModel -export type SpecialDeal = InferSelectModel -export type Order = InferSelectModel -export type OrderItem = InferSelectModel -export type Payment = InferSelectModel -export type Notification = InferSelectModel -export type ProductCategory = InferSelectModel -export type CartItem = InferSelectModel -export type Coupon = InferSelectModel - -// Combined types -export type ProductWithSkus = ProductInfo & { - skus: (ProductSku & { features: SkuFeature[] })[] -} - -export type OrderWithItems = Order & { - items: (OrderItem & { sku: ProductSku & { product: ProductInfo } })[] - address: Address - slot: DeliverySlotInfo -} - -export type CartItemWithSku = CartItem & { - sku: ProductSku & { product: ProductInfo } -} diff --git a/packages/db_helper_sqlite/src/helper_methods/banner.ts b/packages/db_helper_sqlite/src/helper_methods/banner.ts deleted file mode 100644 index 67d6250..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/banner.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { db } from '../db/db_index' -import { homeBanners } from '../db/schema' -import { desc, eq } from 'drizzle-orm' - -export interface Banner { - id: number - name: string - imageUrl: string - description: string | null - productIds: number[] | null - redirectUrl: string | null - serialNum: number | null - isActive: boolean - createdAt: Date - lastUpdated: Date -} - -type BannerRow = typeof homeBanners.$inferSelect - -export async function getBanners(): Promise { - const banners = await db.query.homeBanners.findMany({ - orderBy: desc(homeBanners.createdAt), - }) as BannerRow[] - - return banners.map((banner) => ({ - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - })) -} - -export async function getBannerById(id: number): Promise { - const banner = await db.query.homeBanners.findFirst({ - where: eq(homeBanners.id, id), - }) - - if (!banner) return null - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - } -} - -export type CreateBannerInput = Omit - -export async function createBanner(input: CreateBannerInput): Promise { - const [banner] = await db.insert(homeBanners).values({ - name: input.name, - imageUrl: input.imageUrl, - description: input.description, - productIds: input.productIds, - redirectUrl: input.redirectUrl, - serialNum: input.serialNum, - isActive: input.isActive, - }).returning() - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - } -} - -export type UpdateBannerInput = Partial> - -export async function updateBanner(id: number, input: UpdateBannerInput): Promise { - const [banner] = await db.update(homeBanners) - .set({ - ...input, - lastUpdated: new Date(), - }) - .where(eq(homeBanners.id, id)) - .returning() - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - } -} - -export async function deleteBanner(id: number): Promise { - await db.delete(homeBanners).where(eq(homeBanners.id, id)) -} diff --git a/packages/db_helper_sqlite/src/helper_methods/complaint.ts b/packages/db_helper_sqlite/src/helper_methods/complaint.ts deleted file mode 100644 index a58d7b2..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/complaint.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { db } from '../db/db_index' -import { complaints, users } from '../db/schema' -import { eq, desc, lt } from 'drizzle-orm' - -export interface Complaint { - id: number - complaintBody: string - userId: number - orderId: number | null - isResolved: boolean - response: string | null - createdAt: Date - images: string[] | null -} - -export interface ComplaintWithUser extends Complaint { - userName: string | null - userMobile: string | null -} - -export async function getComplaints( - cursor?: number, - limit: number = 20 -): Promise<{ complaints: ComplaintWithUser[]; hasMore: boolean }> { - const whereCondition = cursor ? lt(complaints.id, cursor) : undefined - - const complaintsData = await db - .select({ - id: complaints.id, - complaintBody: complaints.complaintBody, - userId: complaints.userId, - orderId: complaints.orderId, - isResolved: complaints.isResolved, - response: complaints.response, - createdAt: complaints.createdAt, - images: complaints.images, - userName: users.name, - userMobile: users.mobile, - }) - .from(complaints) - .leftJoin(users, eq(complaints.userId, users.id)) - .where(whereCondition) - .orderBy(desc(complaints.id)) - .limit(limit + 1) - - const hasMore = complaintsData.length > limit - const complaintsToReturn = hasMore ? complaintsData.slice(0, limit) : complaintsData - - return { - complaints: complaintsToReturn.map((c) => ({ - id: c.id, - complaintBody: c.complaintBody, - userId: c.userId, - orderId: c.orderId, - isResolved: c.isResolved, - response: c.response, - createdAt: c.createdAt, - images: c.images, - userName: c.userName, - userMobile: c.userMobile, - })), - hasMore, - } -} - -export async function resolveComplaint( - id: number, - response?: string -): Promise { - await db - .update(complaints) - .set({ isResolved: true, response }) - .where(eq(complaints.id, id)) -} diff --git a/packages/db_helper_sqlite/src/helper_methods/const.ts b/packages/db_helper_sqlite/src/helper_methods/const.ts deleted file mode 100644 index 0b9b6c5..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/const.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { db } from '../db/db_index' -import { keyValStore } from '../db/schema' -import { eq } from 'drizzle-orm' -import { castConstValue } from '../lib/const-keys' - -export interface Constant { - key: string - value: any -} - -export async function getAllConstants(): Promise { - const constants = await db.select().from(keyValStore) - - return constants.map(c => ({ - key: c.key, - value: castConstValue(c.key, c.value), - })) -} - -export async function upsertConstants(constants: Constant[]): Promise { - await db.transaction(async (tx) => { - for (const { key, value } of constants) { - const castedValue = castConstValue(key, value) - const existing = await tx.query.keyValStore.findFirst({ - where: eq(keyValStore.key, key), - columns: { key: true }, - }) - - if (existing) { - await tx.update(keyValStore) - .set({ value: castedValue }) - .where(eq(keyValStore.key, key)) - } else { - await tx.insert(keyValStore) - .values({ key, value: castedValue }) - } - } - }) -} diff --git a/packages/db_helper_sqlite/src/helper_methods/coupon.ts b/packages/db_helper_sqlite/src/helper_methods/coupon.ts deleted file mode 100644 index be1c187..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/coupon.ts +++ /dev/null @@ -1,632 +0,0 @@ -import { db } from '../db/db_index'; -import { coupons, reservedCoupons, users } from '../db/schema'; -import { eq, and, like, or, inArray, lt, desc, asc } from 'drizzle-orm'; - -export interface Coupon { - id: number; - couponCode: string; - isUserBased: boolean; - discountPercent: string | null; - flatDiscount: string | null; - minOrder: string | null; - skuIds: number[] | null; - maxValue: string | null; - isApplyForAll: boolean; - validTill: Date | null; - maxLimitForUser: number | null; - exclusiveApply: boolean; - isInvalidated: boolean; - createdAt: Date; - createdBy: number; -} - -export async function getAllCoupons( - cursor?: number, - limit: number = 50, - search?: string -): Promise<{ coupons: any[]; hasMore: boolean }> { - let whereCondition = undefined; - const conditions = []; - - if (cursor) { - conditions.push(lt(coupons.id, cursor)); - } - - if (search && search.trim()) { - conditions.push(like(coupons.couponCode, `%${search}%`)); - } - - if (conditions.length > 0) { - whereCondition = and(...conditions); - } - - const result = await db.query.coupons.findMany({ - where: whereCondition, - with: { - creator: true, - applicableUsers: { - with: { - user: true, - }, - }, - applicableProducts: { - with: { - product: true, - }, - }, - }, - orderBy: (couponsTable: typeof coupons) => [desc(couponsTable.createdAt)], - limit: limit + 1, - }); - - const hasMore = result.length > limit; - const couponsList = hasMore ? result.slice(0, limit) : result; - - return { coupons: couponsList, hasMore }; -} - -export async function getCouponById(id: number): Promise { - const result = await db.query.coupons.findFirst({ - where: eq(coupons.id, id), - with: { - creator: true, - applicableUsers: { - with: { - user: true, - }, - }, - applicableProducts: { - with: { - product: true, - }, - }, - }, - }); - - return result || null; -} - -export async function invalidateCoupon(id: number): Promise { - const result = await db.update(coupons) - .set({ isInvalidated: true }) - .where(eq(coupons.id, id)) - .returning(); - - return result[0]; -} - -export interface CouponValidationResult { - valid: boolean; - message?: string; - discountAmount?: number; - coupon?: Partial; -} - -export async function validateCoupon( - code: string, - userId: number, - orderAmount: number -): Promise { - const coupon = await db.query.coupons.findFirst({ - where: and( - eq(coupons.couponCode, code.toUpperCase()), - eq(coupons.isInvalidated, false) - ), - }); - - if (!coupon) { - return { valid: false, message: "Coupon not found or invalidated" }; - } - - // Check expiry date - if (coupon.validTill && new Date(coupon.validTill) < new Date()) { - return { valid: false, message: "Coupon has expired" }; - } - - // Check if coupon applies to all users or specific user - if (!coupon.isApplyForAll && !coupon.isUserBased) { - return { valid: false, message: "Coupon is not available for use" }; - } - - // Check minimum order amount - const minOrderValue = coupon.minOrder ? parseFloat(coupon.minOrder) : 0; - if (minOrderValue > 0 && orderAmount < minOrderValue) { - return { valid: false, message: `Minimum order amount is ${minOrderValue}` }; - } - - // Calculate discount - let discountAmount = 0; - if (coupon.discountPercent) { - const percent = parseFloat(coupon.discountPercent); - discountAmount = (orderAmount * percent) / 100; - } else if (coupon.flatDiscount) { - discountAmount = parseFloat(coupon.flatDiscount); - } - - // Apply max value limit - const maxValueLimit = coupon.maxValue ? parseFloat(coupon.maxValue) : 0; - if (maxValueLimit > 0 && discountAmount > maxValueLimit) { - discountAmount = maxValueLimit; - } - - return { - valid: true, - discountAmount, - coupon: { - id: coupon.id, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - maxValue: coupon.maxValue, - } - }; -} - -export async function getReservedCoupons( - cursor?: number, - limit: number = 50, - search?: string -): Promise<{ coupons: any[]; hasMore: boolean }> { - let whereCondition = undefined; - const conditions = []; - - if (cursor) { - conditions.push(lt(reservedCoupons.id, cursor)); - } - - if (search && search.trim()) { - conditions.push(or( - like(reservedCoupons.secretCode, `%${search}%`), - like(reservedCoupons.couponCode, `%${search}%`) - )); - } - - if (conditions.length > 0) { - whereCondition = and(...conditions); - } - - const result = await db.query.reservedCoupons.findMany({ - where: whereCondition, - with: { - redeemedUser: true, - creator: true, - }, - orderBy: (reservedCouponsTable: typeof reservedCoupons) => [desc(reservedCouponsTable.createdAt)], - limit: limit + 1, - }); - - const hasMore = result.length > limit; - const couponsList = hasMore ? result.slice(0, limit) : result; - - return { coupons: couponsList, hasMore }; -} - -export interface UserMiniInfo { - id: number; - name: string; - mobile: string | null; -} - -export async function getUsersForCoupon( - search?: string, - limit: number = 20, - offset: number = 0 -): Promise<{ users: UserMiniInfo[] }> { - let whereCondition = undefined; - if (search && search.trim()) { - whereCondition = or( - like(users.name, `%${search}%`), - like(users.mobile, `%${search}%`) - ); - } - - const userList = await db.query.users.findMany({ - where: whereCondition, - columns: { - id: true, - name: true, - mobile: true, - }, - limit: limit, - offset: offset, - orderBy: (usersTable: typeof users) => [asc(usersTable.name)], - }); - - return { - users: userList.map((user: typeof users.$inferSelect) => ({ - id: user.id, - name: user.name || 'Unknown', - mobile: user.mobile, - })) - }; -} - -// ============================================================================ -// BATCH 2: Transaction Methods -// ============================================================================ - -import { couponApplicableUsers, couponApplicableProducts, orders, orderStatus } from '../db/schema'; - -export interface CreateCouponInput { - couponCode: string; - isUserBased: boolean; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - skuIds?: number[] | null; - maxValue?: string; - isApplyForAll: boolean; - validTill?: Date; - maxLimitForUser?: number; - exclusiveApply: boolean; - createdBy: number; -} - -export async function createCouponWithRelations( - input: CreateCouponInput, - applicableUsers?: number[], - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode: input.couponCode, - isUserBased: input.isUserBased, - discountPercent: input.discountPercent, - flatDiscount: input.flatDiscount, - minOrder: input.minOrder, - skuIds: input.skuIds, - createdBy: input.createdBy, - maxValue: input.maxValue, - isApplyForAll: input.isApplyForAll, - validTill: input.validTill, - maxLimitForUser: input.maxLimitForUser, - exclusiveApply: input.exclusiveApply, - }).returning(); - - // Insert applicable users - if (applicableUsers && applicableUsers.length > 0) { - await tx.insert(couponApplicableUsers).values( - applicableUsers.map(userId => ({ - couponId: coupon.id, - userId, - })) - ); - } - - // Insert applicable products - if (applicableProducts && applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(skuId => ({ - couponId: coupon.id, - skuId, - })) - ); - } - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - skuIds: coupon.skuIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export interface UpdateCouponInput { - couponCode?: string; - isUserBased?: boolean; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - skuIds?: number[] | null; - maxValue?: string; - isApplyForAll?: boolean; - validTill?: Date | null; - maxLimitForUser?: number; - exclusiveApply?: boolean; - isInvalidated?: boolean; -} - -export async function updateCouponWithRelations( - id: number, - input: UpdateCouponInput, - applicableUsers?: number[], - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - // Update the coupon - const [coupon] = await tx.update(coupons) - .set({ - ...input, - }) - .where(eq(coupons.id, id)) - .returning(); - - // Update applicable users: delete existing and insert new - if (applicableUsers !== undefined) { - await tx.delete(couponApplicableUsers).where(eq(couponApplicableUsers.couponId, id)); - if (applicableUsers.length > 0) { - await tx.insert(couponApplicableUsers).values( - applicableUsers.map(userId => ({ - couponId: id, - userId, - })) - ); - } - } - - // Update applicable products: delete existing and insert new - if (applicableProducts !== undefined) { - await tx.delete(couponApplicableProducts).where(eq(couponApplicableProducts.couponId, id)); - if (applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(skuId => ({ - couponId: id, - skuId, - })) - ); - } - } - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - skuIds: coupon.skuIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export async function generateCancellationCoupon( - orderId: number, - staffUserId: number, - userId: number, - orderAmount: number, - couponCode: string -): Promise { - return await db.transaction(async (tx) => { - // Calculate expiry date (30 days from now) - const expiryDate = new Date(); - expiryDate.setDate(expiryDate.getDate() + 30); - - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode, - isUserBased: true, - flatDiscount: orderAmount.toString(), - minOrder: orderAmount.toString(), - maxValue: orderAmount.toString(), - validTill: expiryDate, - maxLimitForUser: 1, - createdBy: staffUserId, - isApplyForAll: false, - }).returning(); - - // Insert applicable users - await tx.insert(couponApplicableUsers).values({ - couponId: coupon.id, - userId, - }); - - // Update order_status with refund coupon ID - await tx.update(orderStatus) - .set({ refundCouponId: coupon.id }) - .where(eq(orderStatus.orderId, orderId)); - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - skuIds: coupon.skuIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export interface CreateReservedCouponInput { - secretCode: string; - couponCode: string; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - skuIds?: number[] | null; - maxValue?: string; - validTill?: Date; - maxLimitForUser?: number; - exclusiveApply: boolean; - createdBy: number; -} - -export async function createReservedCouponWithProducts( - input: CreateReservedCouponInput, - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - const [coupon] = await tx.insert(reservedCoupons).values({ - secretCode: input.secretCode, - couponCode: input.couponCode, - discountPercent: input.discountPercent, - flatDiscount: input.flatDiscount, - minOrder: input.minOrder, - skuIds: input.skuIds, - maxValue: input.maxValue, - validTill: input.validTill, - maxLimitForUser: input.maxLimitForUser, - exclusiveApply: input.exclusiveApply, - createdBy: input.createdBy, - }).returning(); - - // Insert applicable products if provided - if (applicableProducts && applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(skuId => ({ - couponId: coupon.id, - skuId, - })) - ); - } - - return coupon; - }); -} - -export async function getOrCreateUserByMobile( - mobile: string -): Promise<{ id: number; mobile: string; name: string | null }> { - return await db.transaction(async (tx) => { - // Check if user exists - let user = await tx.query.users.findFirst({ - where: eq(users.mobile, mobile), - }); - - if (!user) { - // Create new user - const [newUser] = await tx.insert(users).values({ - name: null, - email: null, - mobile, - }).returning(); - user = newUser; - } - - return { - id: user.id, - mobile: user.mobile, - name: user.name, - }; - }); -} - -export async function createCouponForUser( - mobile: string, - couponCode: string, - staffUserId: number -): Promise<{ coupon: Coupon; user: { id: number; mobile: string; name: string | null } }> { - return await db.transaction(async (tx) => { - // Get or create user - let user = await tx.query.users.findFirst({ - where: eq(users.mobile, mobile), - }); - - if (!user) { - const [newUser] = await tx.insert(users).values({ - name: null, - email: null, - mobile, - }).returning(); - user = newUser; - } - - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode, - isUserBased: true, - discountPercent: "20", - minOrder: "1000", - maxValue: "500", - maxLimitForUser: 1, - isApplyForAll: false, - exclusiveApply: false, - createdBy: staffUserId, - validTill: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days from now - }).returning(); - - // Associate coupon with user - await tx.insert(couponApplicableUsers).values({ - couponId: coupon.id, - userId: user.id, - }); - - return { - coupon: { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - skuIds: coupon.skuIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }, - user: { - id: user.id, - mobile: user.mobile, - name: user.name, - }, - }; - }); -} - -// ============================================================================ -// Utility Functions -// ============================================================================ - -export async function checkUsersExist(userIds: number[]): Promise { - const existingUsers = await db.query.users.findMany({ - where: inArray(users.id, userIds), - columns: { id: true }, - }); - return existingUsers.length === userIds.length; -} - -export async function checkCouponExists(couponCode: string): Promise { - const existing = await db.query.coupons.findFirst({ - where: eq(coupons.couponCode, couponCode), - }); - return !!existing; -} - -export async function checkReservedCouponExists(secretCode: string): Promise { - const existing = await db.query.reservedCoupons.findFirst({ - where: eq(reservedCoupons.secretCode, secretCode), - }); - return !!existing; -} - -export async function getOrderWithUser(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - user: true, - }, - }); -} diff --git a/packages/db_helper_sqlite/src/helper_methods/order.ts b/packages/db_helper_sqlite/src/helper_methods/order.ts deleted file mode 100644 index abc5130..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/order.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { db } from '../db/db_index'; -import { orders, orderItems, orderStatus, users, addresses, refunds, complaints, payments } from '../db/schema'; -import { eq, and, gte, lt, desc, inArray, sql } from 'drizzle-orm'; - -export async function updateOrderNotes(orderId: number, adminNotes: string | null): Promise { - const [result] = await db - .update(orders) - .set({ adminNotes }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} - -export async function getOrderWithDetails(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - orderItems: { - with: { - product: { - with: { - unit: true, - }, - }, - }, - }, - user: true, - address: true, - orderStatus: true, - slot: true, - payments: true, - refunds: true, - }, - }); -} - -export async function getFullOrder(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - orderItems: { - with: { - product: { - with: { - unit: true, - }, - }, - }, - }, - user: { - with: { - userDetails: true, - }, - }, - address: true, - orderStatus: true, - slot: true, - payments: true, - refunds: true, - complaints: true, - }, - }); -} - -export async function getOrderDetails(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - orderItems: { - with: { - product: { - with: { - unit: true, - }, - }, - }, - }, - user: true, - address: true, - orderStatus: true, - slot: true, - payments: true, - refunds: true, - complaints: true, - }, - }); -} - -export async function getAllOrders( - limit: number, - cursor?: number, - slotId?: number | null, - filters?: any -): Promise<{ orders: any[]; hasMore: boolean }> { - let whereConditions = []; - - if (cursor) { - whereConditions.push(lt(orders.id, cursor)); - } - - if (slotId) { - whereConditions.push(eq(orders.slotId, slotId)); - } - - // Add filter conditions - if (filters) { - if (filters.packagedFilter === 'packaged') { - whereConditions.push( - sql`${orders.id} IN (SELECT ${orderStatus.orderId} FROM ${orderStatus} WHERE ${orderStatus.isPackaged} = 1)` - ); - } else if (filters.packagedFilter === 'not_packaged') { - whereConditions.push( - sql`${orders.id} IN (SELECT ${orderStatus.orderId} FROM ${orderStatus} WHERE ${orderStatus.isPackaged} = 0)` - ); - } - - if (filters.deliveredFilter === 'delivered') { - whereConditions.push( - sql`${orders.id} IN (SELECT ${orderStatus.orderId} FROM ${orderStatus} WHERE ${orderStatus.isDelivered} = 1)` - ); - } else if (filters.deliveredFilter === 'not_delivered') { - whereConditions.push( - sql`${orders.id} IN (SELECT ${orderStatus.orderId} FROM ${orderStatus} WHERE ${orderStatus.isDelivered} = 0)` - ); - } - - if (filters.flashDeliveryFilter === 'flash') { - whereConditions.push(eq(orders.isFlashDelivery, true)); - } else if (filters.flashDeliveryFilter === 'regular') { - whereConditions.push(eq(orders.isFlashDelivery, false)); - } - } - - const ordersList = await db.query.orders.findMany({ - where: whereConditions.length > 0 ? and(...whereConditions) : undefined, - with: { - orderItems: { - with: { - product: true, - }, - }, - user: true, - orderStatus: true, - slot: true, - }, - orderBy: desc(orders.id), - limit: limit + 1, - }); - - const hasMore = ordersList.length > limit; - return { orders: hasMore ? ordersList.slice(0, limit) : ordersList, hasMore }; -} - -export async function getOrdersBySlotId(slotId: number): Promise { - return await db.query.orders.findMany({ - where: eq(orders.slotId, slotId), - with: { - orderItems: { - with: { - product: true, - }, - }, - user: true, - orderStatus: true, - address: true, - }, - orderBy: desc(orders.createdAt), - }); -} - -export async function updateOrderPackaged(orderId: number, isPackaged: boolean): Promise { - const [result] = await db - .update(orderStatus) - .set({ isPackaged }) - .where(eq(orderStatus.orderId, orderId)) - .returning(); - return result; -} - -export async function updateOrderDelivered(orderId: number, isDelivered: boolean): Promise { - const [result] = await db - .update(orderStatus) - .set({ isDelivered }) - .where(eq(orderStatus.orderId, orderId)) - .returning(); - return result; -} - -export async function updateOrderItemPackaging( - orderItemId: number, - isPackaged: boolean, - isPackageVerified: boolean -): Promise { - await db.update(orderItems) - .set({ is_packaged: isPackaged, is_package_verified: isPackageVerified }) - .where(eq(orderItems.id, orderItemId)); -} - -export async function updateAddressCoords(addressId: number, lat: number, lng: number): Promise { - await db.update(addresses) - .set({ adminLatitude: lat, adminLongitude: lng }) - .where(eq(addresses.id, addressId)); -} - -export async function getOrderStatus(orderId: number): Promise { - return await db.query.orderStatus.findFirst({ - where: eq(orderStatus.orderId, orderId), - }); -} - -export async function cancelOrder(orderId: number, reason: string): Promise { - return await db.transaction(async (tx) => { - const order = await tx.query.orders.findFirst({ - where: eq(orders.id, orderId), - }) - - if (!order) { - return null - } - - await tx.update(orderStatus) - .set({ - isCancelled: true, - cancelReason: reason, - }) - .where(eq(orderStatus.orderId, orderId)) - - return order - }); -} - -export async function getTodaysOrders(slotId?: number): Promise { - const today = new Date(); - today.setHours(0, 0, 0, 0); - const tomorrow = new Date(today); - tomorrow.setDate(tomorrow.getDate() + 1); - - let whereConditions = [ - gte(orders.createdAt, today), - lt(orders.createdAt, tomorrow), - ]; - - if (slotId) { - whereConditions.push(eq(orders.slotId, slotId)); - } - - return await db.query.orders.findMany({ - where: and(...whereConditions), - with: { - orderItems: { - with: { - product: true, - }, - }, - user: true, - orderStatus: true, - }, - orderBy: desc(orders.createdAt), - }); -} - -export async function removeDeliveryCharge(orderId: number): Promise { - const [result] = await db - .update(orders) - .set({ deliveryCharge: '0' }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} diff --git a/packages/db_helper_sqlite/src/helper_methods/product.ts b/packages/db_helper_sqlite/src/helper_methods/product.ts deleted file mode 100644 index 52c3722..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/product.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { db } from '../db/db_index'; -import { productInfo, units, specialDeals, productTags, productReviews, productGroupInfo, productGroupMembership } from '../db/schema'; -import { eq, and, inArray, desc, sql, asc } from 'drizzle-orm'; - -export async function getAllProducts(): Promise { - return await db.query.productInfo.findMany({ - orderBy: productInfo.name, - with: { - unit: true, - store: true, - }, - }); -} - -export async function getProductById(id: number): Promise { - return await db.query.productInfo.findFirst({ - where: eq(productInfo.id, id), - with: { - unit: true, - store: true, - specialDeals: true, - productTags: { - with: { - tag: true, - }, - }, - }, - }); -} - -export async function createProduct(input: any): Promise { - const [product] = await db.insert(productInfo).values(input).returning(); - return product; -} - -export async function updateProduct(id: number, updates: any): Promise { - const [product] = await db.update(productInfo) - .set(updates) - .where(eq(productInfo.id, id)) - .returning(); - return product; -} - -export async function toggleProductOutOfStock(id: number, isOutOfStock: boolean): Promise { - const [product] = await db.update(productInfo) - .set({ isOutOfStock }) - .where(eq(productInfo.id, id)) - .returning(); - return product; -} - -export async function getAllUnits(): Promise { - return await db.query.units.findMany({ - orderBy: units.shortNotation, - }); -} - -export async function getAllProductTags(): Promise { - return await db.query.productTags.findMany({ - with: { - products: { - with: { - product: true, - }, - }, - }, - }); -} - -export async function getProductReviews(productId: number): Promise { - return await db.query.productReviews.findMany({ - where: eq(productReviews.productId, productId), - with: { - user: true, - }, - orderBy: desc(productReviews.reviewTime), - }); -} - -export async function respondToReview(reviewId: number, adminResponse: string): Promise { - await db.update(productReviews) - .set({ adminResponse }) - .where(eq(productReviews.id, reviewId)); -} - -export async function getAllProductGroups(): Promise { - return await db.query.productGroupInfo.findMany({ - with: { - products: { - with: { - product: true, - }, - }, - }, - }); -} - -export async function createProductGroup(name: string): Promise { - const [group] = await db.insert(productGroupInfo).values({ groupName: name }).returning(); - return group; -} - -export async function updateProductGroup(id: number, name: string): Promise { - const [group] = await db.update(productGroupInfo) - .set({ groupName: name }) - .where(eq(productGroupInfo.id, id)) - .returning(); - return group; -} - -export async function deleteProductGroup(id: number): Promise { - await db.delete(productGroupInfo).where(eq(productGroupInfo.id, id)); -} - -export async function addProductToGroup(groupId: number, productId: number): Promise { - await db.insert(productGroupMembership).values({ groupId, productId }); -} - -export async function removeProductFromGroup(groupId: number, productId: number): Promise { - await db.delete(productGroupMembership) - .where(and( - eq(productGroupMembership.groupId, groupId), - eq(productGroupMembership.productId, productId) - )); -} diff --git a/packages/db_helper_sqlite/src/helper_methods/slots.ts b/packages/db_helper_sqlite/src/helper_methods/slots.ts deleted file mode 100644 index 43dfc9a..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/slots.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { db } from '../db/db_index'; -import { deliverySlotInfo, productInfo, vendorSnippets } from '../db/schema'; -import { eq, and, inArray, desc } from 'drizzle-orm'; - -export async function getAllSlots(): Promise { - const slots = await db.query.deliverySlotInfo.findMany({ - orderBy: desc(deliverySlotInfo.deliveryTime), - with: { - vendorSnippets: true, - }, - }); - - // Fetch products for all slots - const allProductIds = new Set(); - for (const slot of slots) { - for (const productId of (slot.productIds || [])) { - allProductIds.add(productId); - } - } - - const productIdsArray = Array.from(allProductIds); - const productsData = productIdsArray.length > 0 - ? await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIdsArray), - }) - : []; - - const productMap = new Map(productsData.map(p => [p.id, p])); - - return slots.map(slot => ({ - ...slot, - products: (slot.productIds || []) - .map(productId => productMap.get(productId)) - .filter((p): p is NonNullable => p != null), - })); -} - -export async function getSlotById(id: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, id), - with: { - vendorSnippets: { - with: { - slot: true, - }, - }, - }, - }); - - if (!slot) { - return null; - } - - // Fetch products for this slot - const productIds = slot.productIds || []; - const productIdSet = new Set(productIds) - // const productsData = productIds.length > 0 - // ? await db.query.productInfo.findMany({ - // where: inArray(productInfo.id, productIds), - // }) - // : []; - - let productsData = productIds.length > 0 ? await db.query.productInfo.findMany({}) : []; - productsData = productsData.filter(item => productIdSet.has(item.id)) - - return { - ...slot, - products: productsData, - }; -} - -export async function createSlot(input: any): Promise { - const [slot] = await db.insert(deliverySlotInfo).values(input).returning(); - return slot; -} - -export async function updateSlot(id: number, updates: any): Promise { - const [slot] = await db.update(deliverySlotInfo) - .set(updates) - .where(eq(deliverySlotInfo.id, id)) - .returning(); - return slot; -} - -export async function deleteSlot(id: number): Promise { - await db.delete(deliverySlotInfo).where(eq(deliverySlotInfo.id, id)); -} - -export async function getSlotProducts(slotId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - return []; - } - - const productIds = slot.productIds || []; - if (productIds.length === 0) { - return []; - } - - return await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - }); -} - -export async function addProductToSlot(slotId: number, productId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - throw new Error(`Slot ${slotId} not found`); - } - - const currentProductIds = slot.productIds || []; - if (!currentProductIds.includes(productId)) { - await db.update(deliverySlotInfo) - .set({ productIds: [...currentProductIds, productId] }) - .where(eq(deliverySlotInfo.id, slotId)); - } -} - -export async function removeProductFromSlot(slotId: number, productId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - throw new Error(`Slot ${slotId} not found`); - } - - const currentProductIds = slot.productIds || []; - const updatedProductIds = currentProductIds.filter(id => id !== productId); - - await db.update(deliverySlotInfo) - .set({ productIds: updatedProductIds }) - .where(eq(deliverySlotInfo.id, slotId)); -} - -export async function clearSlotProducts(slotId: number): Promise { - await db.update(deliverySlotInfo) - .set({ productIds: [] }) - .where(eq(deliverySlotInfo.id, slotId)); -} - -export async function updateSlotCapacity(slotId: number, maxCapacity: number): Promise { - const [slot] = await db.update(deliverySlotInfo) - .set({ isCapacityFull: Boolean(maxCapacity) }) - .where(eq(deliverySlotInfo.id, slotId)) - .returning(); - return slot; -} - -export async function getSlotDeliverySequence(slotId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - columns: { - deliverySequence: true, - }, - }); - return slot?.deliverySequence || null; -} - -export async function updateSlotDeliverySequence(slotId: number, sequence: any): Promise { - await db.update(deliverySlotInfo) - .set({ deliverySequence: sequence }) - .where(eq(deliverySlotInfo.id, slotId)); -} diff --git a/packages/db_helper_sqlite/src/helper_methods/staff-user.ts b/packages/db_helper_sqlite/src/helper_methods/staff-user.ts deleted file mode 100644 index 50c3bec..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/staff-user.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { db } from '../db/db_index'; -import { staffUsers, staffRoles, users, userDetails, orders } from '../db/schema'; -import { eq, or, and, lt, desc, like } from 'drizzle-orm'; - -export interface StaffUser { - id: number; - name: string; - password: string; - staffRoleId: number; - createdAt: Date; -} - -export async function getStaffUserByName(name: string): Promise { - const staff = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - - return staff || null; -} - -export async function getAllStaff(): Promise { - const staff = await db.query.staffUsers.findMany({ - columns: { - id: true, - name: true, - }, - with: { - role: { - with: { - rolePermissions: { - with: { - permission: true, - }, - }, - }, - }, - }, - }); - - return staff; -} - -export async function getStaffByName(name: string): Promise { - const staff = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - return staff || null; -} - -export async function getAllUsers( - cursor?: number, - limit: number = 20, - search?: string -): Promise<{ users: any[]; hasMore: boolean }> { - let whereCondition = undefined; - - if (search) { - whereCondition = or( - like(users.name, `%${search}%`), - like(users.email, `%${search}%`), - like(users.mobile, `%${search}%`) - ); - } - - if (cursor) { - const cursorCondition = lt(users.id, cursor); - whereCondition = whereCondition ? and(whereCondition, cursorCondition) : cursorCondition; - } - - const allUsers = await db.query.users.findMany({ - where: whereCondition, - with: { - userDetails: true, - }, - orderBy: desc(users.id), - limit: limit + 1, - }); - - const hasMore = allUsers.length > limit; - const usersToReturn = hasMore ? allUsers.slice(0, limit) : allUsers; - - return { users: usersToReturn, hasMore }; -} - -export async function getUserWithDetails(userId: number): Promise { - const user = await db.query.users.findFirst({ - where: eq(users.id, userId), - with: { - userDetails: true, - orders: { - orderBy: desc(orders.createdAt), - limit: 1, - }, - }, - }); - - return user || null; -} - -export async function updateUserSuspension(userId: number, isSuspended: boolean): Promise { - await db - .insert(userDetails) - .values({ userId, isSuspended }) - .onConflictDoUpdate({ - target: userDetails.userId, - set: { isSuspended }, - }); -} - -export async function checkStaffUserExists(name: string): Promise { - const existingUser = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - return !!existingUser; -} - -export async function checkStaffRoleExists(roleId: number): Promise { - const role = await db.query.staffRoles.findFirst({ - where: eq(staffRoles.id, roleId), - }); - return !!role; -} - -export async function createStaffUser( - name: string, - password: string, - roleId: number -): Promise { - const [newUser] = await db.insert(staffUsers).values({ - name: name.trim(), - password, - staffRoleId: roleId, - }).returning(); - - return { - id: newUser.id, - name: newUser.name, - password: newUser.password, - staffRoleId: newUser.staffRoleId ?? roleId, - createdAt: newUser.createdAt, - }; -} - -export async function getAllRoles(): Promise { - const roles = await db.query.staffRoles.findMany({ - columns: { - id: true, - roleName: true, - }, - }); - - return roles; -} diff --git a/packages/db_helper_sqlite/src/helper_methods/store.ts b/packages/db_helper_sqlite/src/helper_methods/store.ts deleted file mode 100644 index c5ffe44..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/store.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { db } from '../db/db_index'; -import { storeInfo, productInfo } from '../db/schema'; -import { eq, inArray } from 'drizzle-orm'; - -export interface Store { - id: number; - name: string; - description: string | null; - imageUrl: string | null; - owner: number; - createdAt: Date; - updatedAt: Date; -} - -export async function getAllStores(): Promise { - const stores = await db.query.storeInfo.findMany({ - with: { - owner: true, - }, - }); - - return stores; -} - -export async function getStoreById(id: number): Promise { - const store = await db.query.storeInfo.findFirst({ - where: eq(storeInfo.id, id), - with: { - owner: true, - }, - }); - - return store || null; -} - -export interface CreateStoreInput { - name: string; - description?: string; - imageUrl?: string; - owner: number; -} - -export async function createStore( - input: CreateStoreInput, - products?: number[] -): Promise { - const [newStore] = await db - .insert(storeInfo) - .values({ - name: input.name, - description: input.description, - imageUrl: input.imageUrl, - owner: input.owner, - }) - .returning(); - - // Assign selected products to this store - if (products && products.length > 0) { - await db - .update(productInfo) - .set({ storeId: newStore.id }) - .where(inArray(productInfo.id, products)); - } - - return { - id: newStore.id, - name: newStore.name, - description: newStore.description, - imageUrl: newStore.imageUrl, - owner: newStore.owner, - createdAt: newStore.createdAt, - updatedAt: newStore.createdAt, - }; -} - -export interface UpdateStoreInput { - name?: string; - description?: string; - imageUrl?: string; - owner?: number; -} - -export async function updateStore( - id: number, - input: UpdateStoreInput, - products?: number[] -): Promise { - const [updatedStore] = await db - .update(storeInfo) - .set({ - ...input, - }) - .where(eq(storeInfo.id, id)) - .returning(); - - if (!updatedStore) { - throw new Error("Store not found"); - } - - // Update products if provided - if (products !== undefined) { - // First, set storeId to null for products not in the list but currently assigned to this store - await db - .update(productInfo) - .set({ storeId: null }) - .where(eq(productInfo.storeId, id)); - - // Then, assign the selected products to this store - if (products.length > 0) { - await db - .update(productInfo) - .set({ storeId: id }) - .where(inArray(productInfo.id, products)); - } - } - - return { - id: updatedStore.id, - name: updatedStore.name, - description: updatedStore.description, - imageUrl: updatedStore.imageUrl, - owner: updatedStore.owner, - createdAt: updatedStore.createdAt, - updatedAt: updatedStore.createdAt, - }; -} - -export async function deleteStore(id: number): Promise<{ message: string }> { - return await db.transaction(async (tx) => { - // First, update all products of this store to set storeId to null - await tx - .update(productInfo) - .set({ storeId: null }) - .where(eq(productInfo.storeId, id)); - - // Then delete the store - const [deletedStore] = await tx - .delete(storeInfo) - .where(eq(storeInfo.id, id)) - .returning(); - - if (!deletedStore) { - throw new Error("Store not found"); - } - - return { - message: "Store deleted successfully", - }; - }); -} diff --git a/packages/db_helper_sqlite/src/helper_methods/user.ts b/packages/db_helper_sqlite/src/helper_methods/user.ts deleted file mode 100644 index c51ee27..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/user.ts +++ /dev/null @@ -1,270 +0,0 @@ -import { db } from '../db/db_index'; -import { users, userDetails, orders, orderItems, complaints, notifCreds, unloggedUserTokens, userIncidents, orderStatus } from '../db/schema'; -import { eq, sql, desc, asc, count, max, inArray, like } from 'drizzle-orm'; - -export async function createUserByMobile(mobile: string): Promise { - const [newUser] = await db - .insert(users) - .values({ - name: null, - email: null, - mobile, - }) - .returning(); - - return newUser; -} - -export async function getUserByMobile(mobile: string): Promise { - const [existingUser] = await db - .select() - .from(users) - .where(eq(users.mobile, mobile)) - .limit(1); - - return existingUser || null; -} - -export async function getUnresolvedComplaintsCount(): Promise { - const result = await db - .select({ count: count(complaints.id) }) - .from(complaints) - .where(eq(complaints.isResolved, false)); - - return result[0]?.count || 0; -} - -export async function getAllUsersWithFilters( - limit: number, - cursor?: number, - search?: string -): Promise<{ users: any[]; hasMore: boolean }> { - const whereConditions = []; - - if (search && search.trim()) { - whereConditions.push(sql`${users.mobile} LIKE ${`%${search.trim()}%`}`); - } - - if (cursor) { - whereConditions.push(sql`${users.id} > ${cursor}`); - } - - const usersList = await db - .select({ - id: users.id, - name: users.name, - mobile: users.mobile, - createdAt: users.createdAt, - }) - .from(users) - .where(whereConditions.length > 0 ? sql.join(whereConditions, sql` AND `) : undefined) - .orderBy(asc(users.id)) - .limit(limit + 1); - - const hasMore = usersList.length > limit; - const usersToReturn = hasMore ? usersList.slice(0, limit) : usersList; - - return { users: usersToReturn, hasMore }; -} - -export async function getOrderCountsByUserIds(userIds: number[]): Promise<{ userId: number; totalOrders: number }[]> { - if (userIds.length === 0) return []; - - return await db - .select({ - userId: orders.userId, - totalOrders: count(orders.id), - }) - .from(orders) - .where(sql`${orders.userId} IN (${sql.join(userIds, sql`, `)})`) - .groupBy(orders.userId); -} - -export async function getLastOrdersByUserIds(userIds: number[]): Promise<{ userId: number; lastOrderDate: Date | null }[]> { - if (userIds.length === 0) return []; - - return await db - .select({ - userId: orders.userId, - lastOrderDate: max(orders.createdAt), - }) - .from(orders) - .where(sql`${orders.userId} IN (${sql.join(userIds, sql`, `)})`) - .groupBy(orders.userId); -} - -export async function getSuspensionStatusesByUserIds(userIds: number[]): Promise<{ userId: number; isSuspended: boolean }[]> { - if (userIds.length === 0) return []; - - return await db - .select({ - userId: userDetails.userId, - isSuspended: userDetails.isSuspended, - }) - .from(userDetails) - .where(sql`${userDetails.userId} IN (${sql.join(userIds, sql`, `)})`); -} - -export async function getUserBasicInfo(userId: number): Promise { - const user = await db - .select({ - id: users.id, - name: users.name, - mobile: users.mobile, - createdAt: users.createdAt, - }) - .from(users) - .where(eq(users.id, userId)) - .limit(1); - - return user[0] || null; -} - -export async function getUserSuspensionStatus(userId: number): Promise { - const userDetail = await db - .select({ - isSuspended: userDetails.isSuspended, - }) - .from(userDetails) - .where(eq(userDetails.userId, userId)) - .limit(1); - - return userDetail[0]?.isSuspended ?? false; -} - -export async function getUserOrders(userId: number): Promise { - return await db - .select({ - id: orders.id, - readableId: orders.readableId, - totalAmount: orders.totalAmount, - createdAt: orders.createdAt, - isFlashDelivery: orders.isFlashDelivery, - }) - .from(orders) - .where(eq(orders.userId, userId)) - .orderBy(desc(orders.createdAt)); -} - -export async function getOrderStatusesByOrderIds(orderIds: number[]): Promise<{ orderId: number; isDelivered: boolean; isCancelled: boolean }[]> { - if (orderIds.length === 0) return []; - - return await db - .select({ - orderId: orderStatus.orderId, - isDelivered: orderStatus.isDelivered, - isCancelled: orderStatus.isCancelled, - }) - .from(orderStatus) - .where(sql`${orderStatus.orderId} IN (${sql.join(orderIds, sql`, `)})`); -} - -export async function getItemCountsByOrderIds(orderIds: number[]): Promise<{ orderId: number; itemCount: number }[]> { - if (orderIds.length === 0) return []; - - return await db - .select({ - orderId: orderItems.orderId, - itemCount: count(orderItems.id), - }) - .from(orderItems) - .where(sql`${orderItems.orderId} IN (${sql.join(orderIds, sql`, `)})`) - .groupBy(orderItems.orderId); -} - -export async function upsertUserSuspension(userId: number, isSuspended: boolean): Promise { - const existingDetail = await db - .select({ id: userDetails.id }) - .from(userDetails) - .where(eq(userDetails.userId, userId)) - .limit(1); - - if (existingDetail.length > 0) { - await db - .update(userDetails) - .set({ isSuspended }) - .where(eq(userDetails.userId, userId)); - } else { - await db - .insert(userDetails) - .values({ - userId, - isSuspended, - }); - } -} - -export async function searchUsers(search?: string): Promise { - if (search && search.trim()) { - return await db - .select({ - id: users.id, - name: users.name, - mobile: users.mobile, - }) - .from(users) - .where(sql`${users.mobile} LIKE ${`%${search.trim()}%`} OR ${users.name} LIKE ${`%${search.trim()}%`}`); - } else { - return await db - .select({ - id: users.id, - name: users.name, - mobile: users.mobile, - }) - .from(users); - } -} - -export async function getAllNotifCreds(): Promise<{ userId: number }[]> { - return await db - .select({ userId: notifCreds.userId }) - .from(notifCreds); -} - -export async function getAllUnloggedTokens(): Promise<{ token: string }[]> { - return await db - .select({ token: unloggedUserTokens.token }) - .from(unloggedUserTokens); -} - -export async function getNotifTokensByUserIds(userIds: number[]): Promise<{ token: string }[]> { - return await db - .select({ token: notifCreds.token }) - .from(notifCreds) - .where(inArray(notifCreds.userId, userIds)); -} - -export async function getUserIncidentsWithRelations(userId: number): Promise { - return await db.query.userIncidents.findMany({ - where: eq(userIncidents.userId, userId), - with: { - order: { - with: { - orderStatus: true, - }, - }, - addedBy: true, - }, - orderBy: desc(userIncidents.dateAdded), - }); -} - -export async function createUserIncident( - userId: number, - orderId: number | undefined, - adminComment: string | undefined, - adminUserId: number, - negativityScore: number | undefined -): Promise { - const [incident] = await db.insert(userIncidents) - .values({ - userId, - orderId, - adminComment, - addedBy: adminUserId, - negativityScore, - }) - .returning(); - - return incident; -} diff --git a/packages/db_helper_sqlite/src/helper_methods/vendor-snippets.ts b/packages/db_helper_sqlite/src/helper_methods/vendor-snippets.ts deleted file mode 100644 index 20d4722..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/vendor-snippets.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { db } from '../db/db_index'; -import { vendorSnippets, deliverySlotInfo, productInfo, orders, orderItems, orderStatus } from '../db/schema'; -import { eq, and, inArray, gt, sql, asc, desc } from 'drizzle-orm'; - -export async function checkVendorSnippetExists(snippetCode: string): Promise { - const existingSnippet = await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.snippetCode, snippetCode), - }); - return !!existingSnippet; -} - -export async function getVendorSnippetById(id: number): Promise { - return await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.id, id), - with: { - slot: true, - }, - }); -} - -export async function getVendorSnippetByCode(snippetCode: string): Promise { - return await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.snippetCode, snippetCode), - }); -} - -export async function getAllVendorSnippets(): Promise { - return await db.query.vendorSnippets.findMany({ - with: { - slot: true, - }, - orderBy: desc(vendorSnippets.createdAt), - }); -} - -export interface CreateVendorSnippetInput { - snippetCode: string; - slotId?: number; - productIds: number[]; - isPermanent: boolean; - validTill?: Date; -} - -export async function createVendorSnippet(input: CreateVendorSnippetInput): Promise { - const [result] = await db.insert(vendorSnippets).values({ - snippetCode: input.snippetCode, - slotId: input.slotId, - productIds: input.productIds, - isPermanent: input.isPermanent, - validTill: input.validTill, - }).returning(); - - return result; -} - -export async function updateVendorSnippet(id: number, updates: any): Promise { - const [result] = await db.update(vendorSnippets) - .set(updates) - .where(eq(vendorSnippets.id, id)) - .returning(); - - return result; -} - -export async function deleteVendorSnippet(id: number): Promise { - await db.delete(vendorSnippets) - .where(eq(vendorSnippets.id, id)); -} - -export async function getProductsByIds(productIds: number[]): Promise { - return await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - columns: { id: true, name: true }, - }); -} - -export async function getVendorSlotById(slotId: number): Promise { - return await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); -} - -export async function getVendorOrdersBySlotId(slotId: number): Promise { - return await db.query.orders.findMany({ - where: eq(orders.slotId, slotId), - with: { - orderItems: { - with: { - product: { - with: { - unit: true, - }, - }, - }, - }, - orderStatus: true, - user: true, - slot: true, - }, - orderBy: desc(orders.createdAt), - }); -} - -export async function getOrderItemsByOrderIds(orderIds: number[]): Promise { - return await db.query.orderItems.findMany({ - where: inArray(orderItems.orderId, orderIds), - with: { - product: { - with: { - unit: true, - }, - }, - }, - }); -} - -export async function getOrderStatusByOrderIds(orderIds: number[]): Promise { - return await db.query.orderStatus.findMany({ - where: inArray(orderStatus.orderId, orderIds), - }); -} - -export async function updateVendorOrderItemPackaging(orderItemId: number, isPackaged: boolean, isPackageVerified: boolean): Promise { - await db.update(orderItems) - .set({ - is_packaged: isPackaged, - is_package_verified: isPackageVerified, - }) - .where(eq(orderItems.id, orderItemId)); -} diff --git a/packages/db_helper_sqlite/src/user-apis/order.ts b/packages/db_helper_sqlite/src/user-apis/order.ts index aa82550..0db4c32 100644 --- a/packages/db_helper_sqlite/src/user-apis/order.ts +++ b/packages/db_helper_sqlite/src/user-apis/order.ts @@ -24,32 +24,6 @@ import { coerceDate } from '../lib/date' import { runBatched } from '../lib/run-batched' import { composeSkuName, composeUnitNotation } from '../lib/sku-features' -export interface OrderItemInput { - productId: number - quantity: number - slotId: number | null -} - -export interface PlaceOrderInput { - userId: number - selectedItems: OrderItemInput[] - addressId: number - paymentMethod: 'online' | 'cod' - couponId?: number - userNotes?: string - isFlash?: boolean -} - -export interface OrderGroupData { - slotId: number | null - items: Array<{ - productId: number - quantity: number - slotId: number | null - product: typeof productInfo.$inferSelect - }> -} - export interface PlacedOrder { id: number userId: number diff --git a/packages/db_helper_sqlite/src/user-apis/tags.ts b/packages/db_helper_sqlite/src/user-apis/tags.ts deleted file mode 100644 index 2b32f81..0000000 --- a/packages/db_helper_sqlite/src/user-apis/tags.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { db } from '../db/db_index' -import { productTags } from '../db/schema' -import { eq } from 'drizzle-orm' - -export async function getAllTags(): Promise { - return db.query.productTags.findMany({ - with: { - // products: { - // with: { - // product: true, - // }, - // }, - }, - }) -} - -export async function getTagById(id: number): Promise { - return db.query.productTags.findFirst({ - where: eq(productTags.id, id), - with: { - // products: { - // with: { - // product: true, - // }, - // }, - }, - }) -} diff --git a/packages/shared/index.ts b/packages/shared/index.ts index 79d2335..27fd3b5 100644 --- a/packages/shared/index.ts +++ b/packages/shared/index.ts @@ -7,7 +7,5 @@ export const CACHE_FILENAMES = { banners: 'banners.json', } as const -export type CacheFilename = typeof CACHE_FILENAMES[keyof typeof CACHE_FILENAMES] - // Re-export all types from the types folder export * from './types'