import type { RequestHandler } from './$types'; import prisma from '$lib/server/prisma'; import { v2 as cloudinary } from 'cloudinary'; import { CLOUDINARY_URL } from '$env/static/private'; // Configure Cloudinary using the URL if (!CLOUDINARY_URL) { throw new Error('Missing CLOUDINARY_URL environment variable'); } cloudinary.config({ url: CLOUDINARY_URL }); export const POST: RequestHandler = async ({ request, params }) => { const formData = await request.formData(); const name = (formData.get('name') as string | null)?.trim() ?? ''; const description = (formData.get('description') as string | null)?.trim() || null; const instructions = (formData.get('instructions') as string | null)?.trim() || null; const time = ((formData.get('time') as string | null)?.trim() || 'Medium') as string; const station = ((formData.get('station') as string | null)?.trim() || 'Pans') as string; const photo = formData.get('photo') as File | null; const parsedIngredientsRaw = formData.get('parsedIngredients') as string | null; let parsedIngredients: Array<{ name: string; quantity: number | null; unit: string | null; prep: string | null }> = []; if (!name) { return new Response(JSON.stringify({ type: 'error', message: 'Name is required' }), { status: 400 }); } if (parsedIngredientsRaw) { try { parsedIngredients = JSON.parse(parsedIngredientsRaw); } catch (error) { console.error('Failed to parse ingredients JSON:', error); return new Response(JSON.stringify({ type: 'error', message: 'Invalid ingredients format' }), { status: 400 }); } } let photoUrl: string | null = null; // Handle photo upload if provided if (photo && photo.size > 0) { try { // Convert File to buffer const arrayBuffer = await photo.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); // Upload to Cloudinary const result = await new Promise<{ secure_url: string }>((resolve, reject) => { cloudinary.uploader.upload_stream( { folder: 'chef-bible/recipes', resource_type: 'auto', transformation: [ { width: 800, height: 600, crop: 'limit' }, { quality: 'auto:good' } ] }, (error, result) => { if (error) reject(error); else if (result) resolve(result as { secure_url: string }); else reject(new Error('No result from Cloudinary')); } ).end(buffer); }); photoUrl = result.secure_url; } catch (error) { console.error('Failed to upload photo to Cloudinary:', error); return new Response(JSON.stringify({ type: 'error', message: 'Failed to upload photo' }), { status: 500 }); } } // Get existing recipe to check if we need to preserve the existing photo const existingRecipe = await prisma.recipe.findUnique({ where: { id: params.id } }); if (!existingRecipe) { return new Response(JSON.stringify({ type: 'error', message: 'Recipe not found' }), { status: 404 }); } // Update recipe const recipe = await prisma.recipe.update({ where: { id: params.id }, data: { name, description, instructions, photoUrl: photoUrl || existingRecipe.photoUrl, // Keep existing photo if no new one uploaded time, station } }); // Delete existing recipe ingredients await prisma.recipeIngredient.deleteMany({ where: { recipeId: params.id } }); if (parsedIngredients.length > 0) { // Upsert ingredients first const ingredientNames = parsedIngredients.map(i => i.name).filter(Boolean); const existingIngredients = await prisma.ingredient.findMany({ where: { name: { in: ingredientNames } } }); const existingNames = new Set(existingIngredients.map(i => i.name)); const createIngredients = ingredientNames .filter(n => !existingNames.has(n)) .map(n => ({ name: n })); if (createIngredients.length) { await prisma.ingredient.createMany({ data: createIngredients }); } // Get all ingredients with ids const allIngredients = await prisma.ingredient.findMany({ where: { name: { in: ingredientNames } } }); const ingredientMap = new Map(allIngredients.map(i => [i.name, i.id])); // Bulk create recipeIngredient const recipeIngredientsData = parsedIngredients .filter(i => i.name) .map(i => ({ recipeId: recipe.id, ingredientId: ingredientMap.get(i.name)!, quantity: i.quantity, unit: i.unit, prep: i.prep })); if (recipeIngredientsData.length) { await prisma.recipeIngredient.createMany({ data: recipeIngredientsData }); } } return new Response(JSON.stringify({ type: 'success', status: 200, location: `/recipe/${recipe.id}` }), { status: 200, headers: { 'Content-Type': 'application/json' } }); }; export const DELETE: RequestHandler = async ({ params }) => { try { // Check if recipe exists const existingRecipe = await prisma.recipe.findUnique({ where: { id: params.id } }); if (!existingRecipe) { return new Response(JSON.stringify({ type: 'error', message: 'Recipe not found' }), { status: 404 }); } // Delete recipe ingredients first (due to foreign key constraints) await prisma.recipeIngredient.deleteMany({ where: { recipeId: params.id } }); // Delete the recipe await prisma.recipe.delete({ where: { id: params.id } }); return new Response(JSON.stringify({ type: 'success', message: 'Recipe deleted successfully' }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (error) { console.error('Error deleting recipe:', error); return new Response(JSON.stringify({ type: 'error', message: 'Failed to delete recipe' }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } };