You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

202 lines
5.2 KiB

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<CartSummary> {
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<void> {
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<boolean> {
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
}
}