import type { H3Event } from 'h3' import { and, eq, inArray } from 'drizzle-orm' import { carts, cartItems, products } from '../database/schema' // Re-export shared types export type { CartItemWithProduct, CartSummary } from '~/types/cart' import type { CartItemWithProduct, CartSummary } from '~/types/cart' /** * Get or create a cart for the current user/session * * @param event - H3 event object * @returns Cart record */ export async function getOrCreateCart(event: H3Event) { const db = useDatabase() const { user } = await getUserSession(event) if (user) { // Authenticated user - find or create cart by userId let cart = await db.query.carts.findFirst({ where: eq(carts.userId, user.id), }) if (!cart) { // Create new cart for user const [newCart] = await db .insert(carts) .values({ userId: user.id, sessionId: '', // Empty for user carts (not used) }) .returning() cart = newCart } return cart } else { // Guest user - find or create cart by sessionId const sessionId = getOrCreateSessionId(event) let cart = await db.query.carts.findFirst({ where: and(eq(carts.sessionId, sessionId), eq(carts.userId, null)), }) if (!cart) { // Create new cart for guest const [newCart] = await db .insert(carts) .values({ userId: null, sessionId, }) .returning() cart = newCart } return cart } } /** * Get cart with all items and product details * * Automatically filters out unavailable products (inactive or out of stock) * and removes them from the cart. * * @param cartId - Cart UUID * @returns Cart summary with items, totals, and removed items */ export async function getCartWithItems(cartId: string): Promise { const db = useDatabase() // Fetch cart const cart = await db.query.carts.findFirst({ where: eq(carts.id, cartId), }) if (!cart) { throw createError({ statusCode: 404, statusMessage: 'Cart not found', }) } // Fetch cart items with product details const items = await db.query.cartItems.findMany({ where: eq(cartItems.cartId, cartId), with: { product: true, }, }) // Separate available and unavailable items const availableItems: CartItemWithProduct[] = [] const unavailableItemIds: string[] = [] const removedProductNames: string[] = [] for (const item of items) { // Check if product is available const isAvailable = item.product.active && item.product.stockQuantity >= item.quantity if (isAvailable) { // Add to available items with subtotal calculation availableItems.push({ id: item.id, cartId: item.cartId, productId: item.productId, quantity: item.quantity, addedAt: item.addedAt, product: { id: item.product.id, name: item.product.name, description: item.product.description, price: item.product.price, stockQuantity: item.product.stockQuantity, active: item.product.active, category: item.product.category, imageUrl: item.product.imageUrl, }, subtotal: Number.parseFloat(item.product.price) * item.quantity, }) } else { // Mark for removal unavailableItemIds.push(item.id) removedProductNames.push(item.product.name) } } // Remove unavailable items from cart if (unavailableItemIds.length > 0) { await db.delete(cartItems).where(inArray(cartItems.id, unavailableItemIds)) // Update cart's updatedAt timestamp await db .update(carts) .set({ updatedAt: new Date() }) .where(eq(carts.id, cartId)) } // Calculate total const total = availableItems.reduce((sum, item) => sum + item.subtotal, 0) const itemCount = availableItems.reduce((sum, item) => sum + item.quantity, 0) return { cart, items: availableItems, total, itemCount, ...(removedProductNames.length > 0 && { removedItems: removedProductNames }), } } /** * Update cart's updated_at timestamp * * @param cartId - Cart UUID */ export async function touchCart(cartId: string): Promise { const db = useDatabase() await db .update(carts) .set({ updatedAt: new Date() }) .where(eq(carts.id, cartId)) } /** * Check if a cart item belongs to the current user/session * * @param event - H3 event object * @param cartItemId - Cart item UUID * @returns true if item belongs to current user/session, false otherwise */ export async function verifyCartItemOwnership( event: H3Event, cartItemId: string ): Promise { const db = useDatabase() const { user } = await getUserSession(event) // Fetch cart item with cart details const item = await db.query.cartItems.findFirst({ where: eq(cartItems.id, cartItemId), with: { cart: true, }, }) if (!item) { return false } // Check ownership if (user) { // Authenticated user - check userId match return item.cart.userId === user.id } else { // Guest user - check sessionId match const sessionId = getSessionId(event) return sessionId !== null && item.cart.sessionId === sessionId && item.cart.userId === null } }