freshyo/verifier/apis/banner.ts
2026-03-22 20:20:18 +05:30

176 lines
5.5 KiB
TypeScript

import { z } from 'zod';
import { db } from '@/src/db/db_index'
import { homeBanners } from '@/src/db/schema'
import { eq, and, desc, sql } from 'drizzle-orm';
import { protectedProcedure, router } from '@/src/trpc/trpc-index'
import { extractKeyFromPresignedUrl, scaffoldAssetUrl } from '@/src/lib/s3-client'
import { ApiError } from '@/src/lib/api-error';
import { scheduleStoreInitialization } from '@/src/stores/store-initializer'
export const bannerRouter = router({
// Get all banners
getBanners: protectedProcedure
.query(async () => {
try {
const banners = await db.query.homeBanners.findMany({
orderBy: desc(homeBanners.createdAt), // Order by creation date instead
// Removed product relationship since we now use productIds array
});
// Convert S3 keys to signed URLs for client
const bannersWithSignedUrls = await Promise.all(
banners.map(async (banner) => {
try {
return {
...banner,
imageUrl: banner.imageUrl ? scaffoldAssetUrl(banner.imageUrl) : banner.imageUrl,
// Ensure productIds is always an array
productIds: banner.productIds || [],
};
} catch (error) {
console.error(`Failed to generate signed URL for banner ${banner.id}:`, error);
return {
...banner,
imageUrl: banner.imageUrl, // Keep original on error
// Ensure productIds is always an array
productIds: banner.productIds || [],
};
}
})
);
return {
banners: bannersWithSignedUrls,
};
}
catch(e:any) {
console.log(e)
throw new ApiError(e.message);
}
}),
// Get single banner by ID
getBanner: protectedProcedure
.input(z.object({ id: z.number() }))
.query(async ({ input }) => {
const banner = await db.query.homeBanners.findFirst({
where: eq(homeBanners.id, input.id),
// Removed product relationship since we now use productIds array
});
if (banner) {
try {
// Convert S3 key to signed URL for client
if (banner.imageUrl) {
banner.imageUrl = scaffoldAssetUrl(banner.imageUrl);
}
} catch (error) {
console.error(`Failed to generate signed URL for banner ${banner.id}:`, error);
// Keep original imageUrl on error
}
// Ensure productIds is always an array (handle migration compatibility)
if (!banner.productIds) {
banner.productIds = [];
}
}
return banner;
}),
// Create new banner
createBanner: protectedProcedure
.input(z.object({
name: z.string().min(1),
imageUrl: z.string(),
description: z.string().optional(),
productIds: z.array(z.number()).optional(),
redirectUrl: z.string().url().optional(),
// serialNum removed completely
}))
.mutation(async ({ input }) => {
try {
const imageUrl = extractKeyFromPresignedUrl(input.imageUrl)
// const imageUrl = input.imageUrl
const [banner] = await db.insert(homeBanners).values({
name: input.name,
imageUrl: imageUrl,
description: input.description,
productIds: input.productIds || [],
redirectUrl: input.redirectUrl,
serialNum: 999, // Default value, not used
isActive: false, // Default to inactive
}).returning();
// Reinitialize stores to reflect changes
scheduleStoreInitialization()
return banner;
} catch (error) {
console.error('Error creating banner:', error);
throw error; // Re-throw to maintain tRPC error handling
}
}),
// Update banner
updateBanner: protectedProcedure
.input(z.object({
id: z.number(),
name: z.string().min(1).optional(),
imageUrl: z.string().url().optional(),
description: z.string().optional(),
productIds: z.array(z.number()).optional(),
redirectUrl: z.string().url().optional(),
serialNum: z.number().nullable().optional(),
isActive: z.boolean().optional(),
}))
.mutation(async ({ input }) => {
try {
const { id, ...updateData } = input;
const incomingProductIds = input.productIds;
// Extract S3 key from presigned URL if imageUrl is provided
const processedData = {
...updateData,
...(updateData.imageUrl && {
imageUrl: extractKeyFromPresignedUrl(updateData.imageUrl)
}),
};
// Handle serialNum null case
const finalData: any = { ...processedData };
if ('serialNum' in finalData && finalData.serialNum === null) {
// Set to null explicitly
finalData.serialNum = null;
}
const [banner] = await db.update(homeBanners)
.set({ ...finalData, lastUpdated: new Date(), })
.where(eq(homeBanners.id, id))
.returning();
// Reinitialize stores to reflect changes
scheduleStoreInitialization()
return banner;
} catch (error) {
console.error('Error updating banner:', error);
throw error;
}
}),
// Delete banner
deleteBanner: protectedProcedure
.input(z.object({ id: z.number() }))
.mutation(async ({ input }) => {
await db.delete(homeBanners).where(eq(homeBanners.id, input.id));
// Reinitialize stores to reflect changes
scheduleStoreInitialization()
return { success: true };
}),
});