import { router, protectedProcedure } from '@/src/trpc/trpc-index' import { z } from 'zod'; import { db } from '@/src/db/db_index' import { storeInfo, productInfo } from '@/src/db/schema' import { eq, inArray } from 'drizzle-orm'; import { ApiError } from '@/src/lib/api-error' import { extractKeyFromPresignedUrl, deleteImageUtil, scaffoldAssetUrl } from '@/src/lib/s3-client' import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; import { scheduleStoreInitialization } from '@/src/stores/store-initializer' export const storeRouter = router({ getStores: protectedProcedure .query(async ({ ctx }) => { const stores = await db.query.storeInfo.findMany({ with: { owner: true, }, }); Promise.all(stores.map(async store => { if(store.imageUrl) store.imageUrl = scaffoldAssetUrl(store.imageUrl) })).catch((e) => { throw new ApiError("Unable to find store image urls") } ) return { stores, count: stores.length, }; }), getStoreById: protectedProcedure .input(z.object({ id: z.number(), })) .query(async ({ input, ctx }) => { const { id } = input; const store = await db.query.storeInfo.findFirst({ where: eq(storeInfo.id, id), with: { owner: true, }, }); if (!store) { throw new ApiError("Store not found", 404); } store.imageUrl = scaffoldAssetUrl(store.imageUrl); return { store, }; }), createStore: protectedProcedure .input(z.object({ name: z.string().min(1, "Name is required"), description: z.string().optional(), imageUrl: z.string().optional(), owner: z.number().min(1, "Owner is required"), products: z.array(z.number()).optional(), })) .mutation(async ({ input, ctx }) => { const { name, description, imageUrl, owner, products } = input; // const imageKey = imageUrl ? extractKeyFromPresignedUrl(imageUrl) : undefined; const imageKey = imageUrl const [newStore] = await db .insert(storeInfo) .values({ name, description, imageUrl: imageKey, 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)); } // Reinitialize stores to reflect changes scheduleStoreInitialization() return { store: newStore, message: "Store created successfully", }; }), updateStore: protectedProcedure .input(z.object({ id: z.number(), name: z.string().min(1, "Name is required"), description: z.string().optional(), imageUrl: z.string().optional(), owner: z.number().min(1, "Owner is required"), products: z.array(z.number()).optional(), })) .mutation(async ({ input, ctx }) => { const { id, name, description, imageUrl, owner, products } = input; const existingStore = await db.query.storeInfo.findFirst({ where: eq(storeInfo.id, id), }); if (!existingStore) { throw new ApiError("Store not found", 404); } const oldImageKey = existingStore.imageUrl; const newImageKey = imageUrl ? extractKeyFromPresignedUrl(imageUrl) : oldImageKey; // Delete old image only if: // 1. New image provided and keys are different, OR // 2. No new image but old exists (clearing the image) if (oldImageKey && ( (newImageKey && newImageKey !== oldImageKey) || (!newImageKey) )) { try { await deleteImageUtil({keys: [oldImageKey]}); } catch (error) { console.error('Failed to delete old image:', error); // Continue with update even if deletion fails } } const [updatedStore] = await db .update(storeInfo) .set({ name, description, imageUrl: newImageKey, owner, }) .where(eq(storeInfo.id, id)) .returning(); if (!updatedStore) { throw new ApiError("Store not found", 404); } // Update products if provided if (products) { // 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)); } } // Reinitialize stores to reflect changes scheduleStoreInitialization() return { store: updatedStore, message: "Store updated successfully", }; }), deleteStore: protectedProcedure .input(z.object({ storeId: z.number(), })) .mutation(async ({ input, ctx }) => { const { storeId } = input; const result = 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, storeId)); // Then delete the store const [deletedStore] = await tx .delete(storeInfo) .where(eq(storeInfo.id, storeId)) .returning(); if (!deletedStore) { throw new ApiError("Store not found", 404); } return { message: "Store deleted successfully", }; }); // Reinitialize stores to reflect changes (outside transaction) scheduleStoreInitialization() return result; }), });