enh
This commit is contained in:
parent
4d660e945b
commit
5bacc25627
22 changed files with 57406 additions and 876 deletions
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
|
@ -14,7 +14,7 @@ export const createApp = () => {
|
|||
|
||||
// CORS middleware
|
||||
app.use(cors({
|
||||
origin: ['http://localhost:5174', 'http://localhost:4174', 'https://ui.freshyo.in'],
|
||||
origin: ['http://localhost:5174', 'http://localhost:4174', 'https://ui.freshyo.in', 'https://webui.freshyo.in', 'https://app.freshyo.in'],
|
||||
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||
allowHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization', 'Caller-Interface'],
|
||||
credentials: true,
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ export {
|
|||
updateSlotCapacity,
|
||||
getSlotDeliverySequence,
|
||||
updateSlotDeliverySequence,
|
||||
staleSlotsCleanup,
|
||||
// Admin - Staff User
|
||||
getStaffUserByName,
|
||||
getStaffUserById,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
updateSlotDeliverySequence as updateSlotDeliverySequenceInDb,
|
||||
updateSlotProducts as updateSlotProductsInDb,
|
||||
getSlotsProductIds as getSlotsProductIdsInDb,
|
||||
staleSlotsCleanup,
|
||||
} from '@/src/dbService'
|
||||
import type {
|
||||
AdminDeliverySequenceResult,
|
||||
|
|
@ -93,7 +94,7 @@ export const slotsRouter = router({
|
|||
throw new TRPCError({ code: "UNAUTHORIZED", message: "Access denied" });
|
||||
}
|
||||
|
||||
const slots = await getActiveSlotsWithProductsInDb()
|
||||
const slots = await getActiveSlotsWithProductsInDb(20)
|
||||
|
||||
/*
|
||||
// Old implementation - direct DB queries:
|
||||
|
|
@ -362,6 +363,11 @@ export const slotsRouter = router({
|
|||
// Reinitialize stores to reflect changes (outside transaction)
|
||||
await scheduleStoreInitialization()
|
||||
|
||||
// Fire and forget: cleanup stale product slot associations
|
||||
staleSlotsCleanup().catch((error) => {
|
||||
console.error('Failed to cleanup stale slots:', error)
|
||||
})
|
||||
|
||||
return result
|
||||
}),
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ interface RegisterRequest {
|
|||
const generateToken = async (userId: number): Promise<string> => {
|
||||
return await new SignJWT({ userId })
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setExpirationTime('7d')
|
||||
.setExpirationTime('90d')
|
||||
.sign(getEncodedJwtSecret());
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ export default function CartPage({ isFlashDelivery = false }: CartPageProps) {
|
|||
const product = productsById[item.productId];
|
||||
const quantity = quantities[item.id] || item.quantity;
|
||||
const price = isFlashDelivery ? (product?.flashPrice ?? product?.price ?? 0) : (product?.price || 0);
|
||||
return sum + price * quantity;
|
||||
return sum + Number(price) * quantity;
|
||||
}, 0);
|
||||
const dropdownData = useMemo(
|
||||
() =>
|
||||
|
|
@ -442,7 +442,7 @@ export default function CartPage({ isFlashDelivery = false }: CartPageProps) {
|
|||
// }
|
||||
const quantity = quantities[item.id] || item.quantity;
|
||||
const price = isFlashDelivery ? (product?.flashPrice ?? product?.price ?? 0) : (product?.price || 0);
|
||||
const itemPrice = price * quantity;
|
||||
const itemPrice = (Number(price)) * quantity;
|
||||
|
||||
return (
|
||||
<View key={item.id}>
|
||||
|
|
|
|||
1455
apps/web-ui/package-lock.json
generated
1455
apps/web-ui/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -5,9 +5,11 @@
|
|||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 4174",
|
||||
"preview": "npm run build && wrangler dev",
|
||||
"test": "vitest run",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsc --noEmit",
|
||||
"deploy": "npm run build && wrangler deploy",
|
||||
"cf-typegen": "wrangler types"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
|
|
@ -39,6 +41,7 @@
|
|||
"zustand": "^5.0.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/vite-plugin": "^1.36.3",
|
||||
"@tanstack/devtools-vite": "^0.6.0",
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/react": "^19.0.0",
|
||||
|
|
@ -46,6 +49,7 @@
|
|||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^8.0.0",
|
||||
"vitest": "^4.1.5"
|
||||
"vitest": "^4.1.5",
|
||||
"wrangler": "^4.90.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import { createTRPCReact } from '@trpc/react-query'
|
|||
import { createTRPCClient, httpBatchLink } from '@trpc/client'
|
||||
import type { AppRouter } from '@backend/trpc/router'
|
||||
|
||||
const BASE_API_URL = 'http://192.168.100.111:8787'
|
||||
// const BASE_API_URL = 'http://192.168.100.111:8787'
|
||||
export const BASE_API_URL = 'https://worker.freshyo.in'
|
||||
|
||||
export const trpc = createTRPCReact<AppRouter>()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,15 +5,21 @@ import viteReact from '@vitejs/plugin-react'
|
|||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { nitro } from 'nitro/vite'
|
||||
|
||||
import { cloudflare } from "@cloudflare/vite-plugin";
|
||||
|
||||
const config = defineConfig({
|
||||
resolve: { tsconfigPaths: true },
|
||||
server: { port: 4174 },
|
||||
plugins: [
|
||||
devtools(),
|
||||
nitro({ rollupConfig: { external: [/^@sentry\//] } }),
|
||||
tailwindcss(),
|
||||
tanstackStart(),
|
||||
viteReact(),
|
||||
cloudflare({
|
||||
viteEnvironment: {
|
||||
name: "ssr"
|
||||
}
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
|
|
|
|||
12
apps/web-ui/wrangler.jsonc
Normal file
12
apps/web-ui/wrangler.jsonc
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"$schema": "node_modules/wrangler/config-schema.json",
|
||||
"name": "web-ui",
|
||||
"compatibility_date": "2026-05-10",
|
||||
"observability": {
|
||||
"enabled": true
|
||||
},
|
||||
"main": "@tanstack/react-start/server-entry",
|
||||
"compatibility_flags": [
|
||||
"nodejs_compat"
|
||||
]
|
||||
}
|
||||
|
|
@ -118,6 +118,7 @@ export {
|
|||
updateSlotCapacity,
|
||||
getSlotDeliverySequence,
|
||||
updateSlotDeliverySequence,
|
||||
staleSlotsCleanup,
|
||||
} from './src/admin-apis/slots';
|
||||
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
vendorSnippets,
|
||||
productGroupInfo,
|
||||
} from '../db/schema'
|
||||
import { and, asc, desc, eq, gt, inArray } from 'drizzle-orm'
|
||||
import { and, asc, desc, eq, gt, inArray, lt } from 'drizzle-orm'
|
||||
import type {
|
||||
AdminDeliverySlot,
|
||||
AdminSlotWithProducts,
|
||||
|
|
@ -61,11 +61,12 @@ const mapVendorSnippet = (snippet: typeof vendorSnippets.$inferSelect): AdminVen
|
|||
createdAt: snippet.createdAt,
|
||||
})
|
||||
|
||||
export async function getActiveSlotsWithProducts(): Promise<AdminSlotWithProducts[]> {
|
||||
export async function getActiveSlotsWithProducts(limit: number = 20): Promise<AdminSlotWithProducts[]> {
|
||||
const slots = await db.query.deliverySlotInfo
|
||||
.findMany({
|
||||
where: eq(deliverySlotInfo.isActive, true),
|
||||
orderBy: desc(deliverySlotInfo.deliveryTime),
|
||||
limit,
|
||||
with: {
|
||||
productSlots: {
|
||||
with: {
|
||||
|
|
@ -88,6 +89,27 @@ export async function getActiveSlotsWithProducts(): Promise<AdminSlotWithProduct
|
|||
}))
|
||||
}
|
||||
|
||||
export async function staleSlotsCleanup(): Promise<number> {
|
||||
// Get the 20 most recent slot IDs
|
||||
const recentSlots = await db
|
||||
.select({ id: deliverySlotInfo.id })
|
||||
.from(deliverySlotInfo)
|
||||
.orderBy(desc(deliverySlotInfo.id))
|
||||
.limit(20)
|
||||
|
||||
if (recentSlots.length < 20) {
|
||||
return 0 // Less than 20 slots exist, nothing to clean
|
||||
}
|
||||
|
||||
// Get the threshold (the 20th most recent slot ID - minimum of the 20)
|
||||
const threshold = Math.min(...recentSlots.map((s) => s.id))
|
||||
|
||||
// Delete product_slots for all slots older than threshold
|
||||
const result = await db.delete(productSlots).where(lt(productSlots.slotId, threshold))
|
||||
|
||||
return result.rowCount || 0
|
||||
}
|
||||
|
||||
export async function getActiveSlots(): Promise<AdminDeliverySlot[]> {
|
||||
const slots = await db.query.deliverySlotInfo.findMany({
|
||||
where: eq(deliverySlotInfo.isActive, true),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { db } from '../db/db_index'
|
||||
import { deliverySlotInfo, productInfo, productReviews, productSlots, productTags, specialDeals, storeInfo, units, users } from '../db/schema'
|
||||
import { and, desc, eq, gt, inArray, sql } from 'drizzle-orm'
|
||||
import { productInfo, productReviews, specialDeals, storeInfo, units, users } from '../db/schema'
|
||||
import { and, desc, eq, gt, sql } from 'drizzle-orm'
|
||||
import type { UserProductDetailData, UserProductReview } from '@packages/shared'
|
||||
|
||||
const getStringArray = (value: unknown): string[] | null => {
|
||||
|
|
@ -42,25 +42,8 @@ export async function getProductDetailById(productId: number): Promise<UserProdu
|
|||
columns: { id: true, name: true, description: true },
|
||||
}) : null
|
||||
|
||||
const deliverySlotsData = await db
|
||||
.select({
|
||||
id: deliverySlotInfo.id,
|
||||
deliveryTime: deliverySlotInfo.deliveryTime,
|
||||
freezeTime: deliverySlotInfo.freezeTime,
|
||||
})
|
||||
.from(productSlots)
|
||||
.innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id))
|
||||
.where(
|
||||
and(
|
||||
eq(productSlots.productId, productId),
|
||||
eq(deliverySlotInfo.isActive, true),
|
||||
eq(deliverySlotInfo.isCapacityFull, false),
|
||||
gt(deliverySlotInfo.deliveryTime, sql`NOW()`),
|
||||
gt(deliverySlotInfo.freezeTime, sql`NOW()`)
|
||||
)
|
||||
)
|
||||
.orderBy(deliverySlotInfo.deliveryTime)
|
||||
|
||||
// Note: deliverySlots are now fetched from cache in the frontend via useSlots()
|
||||
// This avoids expensive database joins on every product detail view
|
||||
const specialDealsData = await db
|
||||
.select({
|
||||
quantity: specialDeals.quantity,
|
||||
|
|
@ -95,7 +78,7 @@ export async function getProductDetailById(productId: number): Promise<UserProdu
|
|||
productQuantity: product.productQuantity,
|
||||
isFlashAvailable: product.isFlashAvailable,
|
||||
flashPrice: product.flashPrice?.toString() || null,
|
||||
deliverySlots: deliverySlotsData,
|
||||
deliverySlots: [], // Fetched from cache in frontend via useSlots()
|
||||
specialDeals: specialDealsData.map((deal) => ({
|
||||
quantity: deal.quantity.toString(),
|
||||
price: deal.price.toString(),
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ export {
|
|||
updateSlotCapacity,
|
||||
getSlotDeliverySequence,
|
||||
updateSlotDeliverySequence,
|
||||
staleSlotsCleanup,
|
||||
} from './src/admin-apis/slots'
|
||||
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -449,7 +449,7 @@ export async function getAllOrders(input: GetAllOrdersInput): Promise<AdminGetAl
|
|||
flashDeliveryFilter,
|
||||
} = input
|
||||
|
||||
let whereCondition: SQL<unknown> | undefined = eq(orders.id, orders.id)
|
||||
let whereCondition: SQL<unknown> | undefined = undefined
|
||||
if (cursor) {
|
||||
whereCondition = and(whereCondition, lt(orders.id, cursor))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
vendorSnippets,
|
||||
productGroupInfo,
|
||||
} from '../db/schema'
|
||||
import { and, asc, desc, eq, gt, inArray } from 'drizzle-orm'
|
||||
import { and, asc, desc, eq, gt, inArray, lt } from 'drizzle-orm'
|
||||
import type {
|
||||
AdminDeliverySlot,
|
||||
AdminSlotWithProducts,
|
||||
|
|
@ -97,11 +97,12 @@ const mapVendorSnippet = (snippet: typeof vendorSnippets.$inferSelect): AdminVen
|
|||
createdAt: coerceDate(snippet.createdAt) ?? new Date(0),
|
||||
})
|
||||
|
||||
export async function getActiveSlotsWithProducts(): Promise<AdminSlotWithProducts[]> {
|
||||
export async function getActiveSlotsWithProducts(limit: number = 20): Promise<AdminSlotWithProducts[]> {
|
||||
const slots = await db.query.deliverySlotInfo
|
||||
.findMany({
|
||||
where: eq(deliverySlotInfo.isActive, true),
|
||||
orderBy: desc(deliverySlotInfo.deliveryTime),
|
||||
limit,
|
||||
with: {
|
||||
productSlots: {
|
||||
with: {
|
||||
|
|
@ -124,6 +125,27 @@ export async function getActiveSlotsWithProducts(): Promise<AdminSlotWithProduct
|
|||
}))
|
||||
}
|
||||
|
||||
export async function staleSlotsCleanup(): Promise<number> {
|
||||
// Get the 20 most recent slot IDs
|
||||
const recentSlots = await db
|
||||
.select({ id: deliverySlotInfo.id })
|
||||
.from(deliverySlotInfo)
|
||||
.orderBy(desc(deliverySlotInfo.id))
|
||||
.limit(20)
|
||||
|
||||
if (recentSlots.length < 20) {
|
||||
return 0 // Less than 20 slots exist, nothing to clean
|
||||
}
|
||||
|
||||
// Get the threshold (the 20th most recent slot ID - minimum of the 20)
|
||||
const threshold = Math.min(...recentSlots.map((s) => s.id))
|
||||
|
||||
// Delete product_slots for all slots older than threshold
|
||||
const result = await db.delete(productSlots).where(lt(productSlots.slotId, threshold))
|
||||
|
||||
return result.changes || 0
|
||||
}
|
||||
|
||||
export async function getActiveSlots(): Promise<AdminDeliverySlot[]> {
|
||||
const slots = await db.query.deliverySlotInfo.findMany({
|
||||
where: eq(deliverySlotInfo.isActive, true),
|
||||
|
|
|
|||
|
|
@ -42,25 +42,8 @@ export async function getProductDetailById(productId: number): Promise<UserProdu
|
|||
columns: { id: true, name: true, description: true },
|
||||
}) : null
|
||||
|
||||
const deliverySlotsData = await db
|
||||
.select({
|
||||
id: deliverySlotInfo.id,
|
||||
deliveryTime: deliverySlotInfo.deliveryTime,
|
||||
freezeTime: deliverySlotInfo.freezeTime,
|
||||
})
|
||||
.from(productSlots)
|
||||
.innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id))
|
||||
.where(
|
||||
and(
|
||||
eq(productSlots.productId, productId),
|
||||
eq(deliverySlotInfo.isActive, true),
|
||||
eq(deliverySlotInfo.isCapacityFull, false),
|
||||
gt(deliverySlotInfo.deliveryTime, sql`CURRENT_TIMESTAMP`),
|
||||
gt(deliverySlotInfo.freezeTime, sql`CURRENT_TIMESTAMP`)
|
||||
)
|
||||
)
|
||||
.orderBy(deliverySlotInfo.deliveryTime)
|
||||
|
||||
// Note: deliverySlots are now fetched from cache in the frontend via useSlots()
|
||||
// This avoids expensive database joins on every product detail view
|
||||
const specialDealsData = await db
|
||||
.select({
|
||||
quantity: specialDeals.quantity,
|
||||
|
|
@ -95,7 +78,7 @@ export async function getProductDetailById(productId: number): Promise<UserProdu
|
|||
productQuantity: product.productQuantity,
|
||||
isFlashAvailable: product.isFlashAvailable,
|
||||
flashPrice: product.flashPrice?.toString() || null,
|
||||
deliverySlots: deliverySlotsData,
|
||||
deliverySlots: [], // Fetched from cache in frontend via useSlots()
|
||||
specialDeals: specialDealsData.map((deal) => ({
|
||||
quantity: String(deal.quantity ?? '0'),
|
||||
price: String(deal.price ?? '0'),
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ const isDevMode = Constants.executionEnvironment !== "standalone";
|
|||
// const BASE_API_URL = 'http://192.168.1.5:8787';
|
||||
// const BASE_API_URL = 'http://192.168.100.109:8787';
|
||||
// let BASE_API_URL = "https://raw.freshyo.in";
|
||||
// let BASE_API_URL = "https://worker.freshyo.in";
|
||||
// let BASE_API_URL = "https://worker.freshyo.in";
|
||||
// let BASE_API_URL = "https://freshyo.technocracy.ovh";
|
||||
let BASE_API_URL = 'http://192.168.100.111:8787';
|
||||
// let BASE_API_URL = 'http://192.168.29.176:4000';
|
||||
|
|
|
|||
26844
slots.json
Normal file
26844
slots.json
Normal file
File diff suppressed because it is too large
Load diff
145
top_queries.md
Normal file
145
top_queries.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Top Full-Scan Queries
|
||||
|
||||
## 1. Orders with all relations (no filters)
|
||||
|
||||
Source: `packages/db_helper_sqlite/src/admin-apis/order.ts:441-499` — `getAllOrders()`
|
||||
|
||||
```sql
|
||||
select
|
||||
"id",
|
||||
"user_id",
|
||||
"address_id",
|
||||
"slot_id",
|
||||
"is_cod",
|
||||
"is_online_payment",
|
||||
"payment_info_id",
|
||||
"total_amount",
|
||||
"delivery_charge",
|
||||
"readable_id",
|
||||
"admin_notes",
|
||||
"user_notes",
|
||||
"order_group_id",
|
||||
"order_group_proportion",
|
||||
"is_flash_delivery",
|
||||
"created_at",
|
||||
(select json_array("id","name","email","mobile","created_at") from (select * from "users" where "users"."id" = "orders"."user_id" limit 1)) as "user",
|
||||
(select json_array("id","user_id","name","phone","address_line1","address_line2","city","state","pincode","is_default","latitude","longitude","google_maps_url","admin_latitude","admin_longitude","zone_id","created_at") from (select * from "addresses" where "addresses"."id" = "orders"."address_id" limit 1)) as "address",
|
||||
(select json_array("id","delivery_time","freeze_time","is_active","is_flash","is_capacity_full","delivery_sequence","group_ids") from (select * from "delivery_slot_info" where "delivery_slot_info"."id" = "orders"."slot_id" limit 1)) as "slot",
|
||||
(select coalesce(json_group_array(json_array("id","order_id","product_id","quantity","price","discounted_price","is_packaged","is_package_verified",(select json_array("id","name","short_description","long_description","unit_id","price","market_price","images","is_out_of_stock","is_suspended","is_flash_available","flash_price","created_at","increment_step","product_quantity","store_id",(select json_array("id","short_notation","full_name") from (select * from "units" where "units"."id" = "orders_orderItems_product"."unit_id" limit 1))) from (select * from "product_info" where "product_info"."id" = "orders_orderItems"."product_id" limit 1)))),json_array()) from "order_items" where "order_items"."order_id" = "orders"."id") as "orderItems",
|
||||
(select coalesce(json_group_array(json_array("id","order_time","user_id","order_id","is_packaged","is_delivered","is_cancelled","cancel_reason","is_cancelled_by_admin","payment_state","cancellation_user_notes","cancellation_admin_notes","cancellation_reviewed","cancellation_reviewed_at","refund_coupon_id")),json_array()) from "order_status" where "order_status"."order_id" = "orders"."id") as "orderStatus"
|
||||
from "orders"
|
||||
where "orders"."id" = "orders"."id"
|
||||
order by "orders"."created_at" desc
|
||||
limit ?
|
||||
```
|
||||
|
||||
**Why it's bad**: `where "orders"."id" = "orders"."id"` is an always-true condition — it's a no-op placeholder. When no cursor/slot/filters are provided, this scans ALL orders with 6 levels of nested correlated subqueries, then filters out COD/pending payments in-memory after fetching everything.
|
||||
|
||||
---
|
||||
|
||||
## 2. Active slots (no date filter)
|
||||
|
||||
Source: `packages/db_helper_sqlite/src/admin-apis/slots.ts:32-47` — `getActiveSlotsWithProducts()`
|
||||
|
||||
```sql
|
||||
select
|
||||
"id",
|
||||
"delivery_time",
|
||||
"freeze_time",
|
||||
"is_active",
|
||||
"is_flash",
|
||||
"is_capacity_full",
|
||||
"delivery_sequence",
|
||||
"group_ids",
|
||||
(select coalesce(json_group_array(json_array("product_id","slot_id",(select json_array("id","name","images") from (select * from "product_info" where "product_info"."id" = "deliverySlotInfo_productSlots"."product_id" limit 1)))),json_array()) from "product_slots" where "product_slots"."slot_id" = "deliverySlotInfo"."id") as "productSlots"
|
||||
from "delivery_slot_info"
|
||||
where "deliverySlotInfo"."is_active" = ?
|
||||
order by "deliverySlotInfo"."delivery_time" desc
|
||||
```
|
||||
|
||||
**Why it's bad**: Only filters `is_active = true`, no `deliveryTime > now` check. Returns past expired slots. Correlated subquery per slot for products.
|
||||
|
||||
---
|
||||
|
||||
## 3. All slots with products for cache (deep JOINs on every slot)
|
||||
|
||||
Source: `packages/db_helper_sqlite/src/stores/store-helpers.ts:236-258` — `getAllSlotsWithProductsForCache()`
|
||||
|
||||
```sql
|
||||
select
|
||||
"id",
|
||||
"delivery_time",
|
||||
"freeze_time",
|
||||
"is_active",
|
||||
"is_flash",
|
||||
"is_capacity_full",
|
||||
"delivery_sequence",
|
||||
"group_ids",
|
||||
(select coalesce(json_group_array(json_array("product_id","slot_id",(select json_array("id","name","short_description","long_description","unit_id","price","market_price","images","is_out_of_stock","is_suspended","is_flash_available","flash_price","created_at","increment_step","product_quantity","store_id",(select json_array("id","short_notation","full_name") from (select * from "units" where "units"."id" = "deliverySlotInfo_productSlots_product"."unit_id" limit 1)),(select json_array("id","name","description","image_url","created_at","owner") from (select * from "store_info" where "store_info"."id" = "deliverySlotInfo_productSlots_product"."store_id" limit 1))) from (select * from "product_info" where "product_info"."id" = "deliverySlotInfo_productSlots"."product_id" limit 1)))),json_array()) from "product_slots" where "product_slots"."slot_id" = "deliverySlotInfo"."id") as "productSlots"
|
||||
from "delivery_slot_info"
|
||||
where ("deliverySlotInfo"."is_active" = ? and "deliverySlotInfo"."delivery_time" > ?)
|
||||
order by "deliverySlotInfo"."delivery_time" asc
|
||||
```
|
||||
|
||||
**Why it's bad**: Called on every slot lookup (`getSlotById`, `getProductSlots`, `getAllProductsSlots`, `getMultipleProductsSlots`) in `slot-store.ts`. Despite filtering for future active slots, it loads ALL of them with deep nested subqueries (product → unit + store) per product per slot, even when only one slot's data is needed.
|
||||
|
||||
---
|
||||
|
||||
## 4. Vendor orders (no WHERE at all)
|
||||
|
||||
Source: `packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts:171-187` — `getVendorOrders()`
|
||||
|
||||
```sql
|
||||
select ... from "orders"
|
||||
left join "users" ...
|
||||
left join "order_items" ...
|
||||
left join "product_info" ...
|
||||
left join "units" ...
|
||||
order by "orders"."created_at" desc
|
||||
```
|
||||
|
||||
**Why it's bad**: Absolutely no WHERE clause. Fetches every single order with deep JOINs on every call.
|
||||
|
||||
---
|
||||
|
||||
## 5. Product availability (reads ALL products)
|
||||
|
||||
Source: `packages/db_helper_sqlite/src/user-apis/slots.ts:29-46` — `getProductAvailability()`
|
||||
|
||||
```sql
|
||||
select "id", "name", "is_out_of_stock", "is_flash_available"
|
||||
from "product_info"
|
||||
where "product_info"."is_suspended" = ?
|
||||
```
|
||||
|
||||
**Why it's bad**: Called on every `getSlotsWithProducts` page load. Reads every non-suspended product even though only products in active slots are relevant.
|
||||
|
||||
---
|
||||
|
||||
## 6. searchUsers (no search = all users, no limit)
|
||||
|
||||
Source: `packages/db_helper_sqlite/src/admin-apis/user.ts:197-216` — `searchUsers()`
|
||||
|
||||
```sql
|
||||
select "id", "name", "mobile" from "users"
|
||||
-- no WHERE, no LIMIT
|
||||
```
|
||||
|
||||
**Why it's bad**: When called without a search term, returns the entire `users` table with no limit.
|
||||
|
||||
---
|
||||
|
||||
## 7. All cache initialization helpers
|
||||
|
||||
Source: `packages/db_helper_sqlite/src/stores/store-helpers.ts`
|
||||
|
||||
| Function | Line | Query |
|
||||
|----------|------|-------|
|
||||
| `getAllProductsForCache` | :86 | `select ... from product_info inner join units` — no WHERE |
|
||||
| `getAllStoresForCache` | :115 | `select ... from store_info` — no WHERE |
|
||||
| `getAllProductTagsForCache` | :159 | `select ... from product_tags inner join product_tag_info` — no WHERE |
|
||||
| `getAllTagsForCache` | :187 | `select ... from product_tag_info` — no WHERE |
|
||||
| `getAllTagProductMappings` | :200 | `select ... from product_tags` — no WHERE |
|
||||
| `getAllUserNegativityScores` | :269 | `select ... from user_incidents group by user_id` — no WHERE |
|
||||
|
||||
**Why it's bad**: Individually these are meant for cache warming, but they're also called on every cache read miss in `product-store.ts`, doing 5 full table scans per product detail page view.
|
||||
Loading…
Add table
Reference in a new issue