import { getUserIdFromSession } from '../../utils/auth'; import { PLANTING_COST, isCropGrown } from '../../utils/village'; import { CropType } from '@prisma/client'; // --- Handler --- export default defineEventHandler(async (event) => { const userId = await getUserIdFromSession(event); const { fieldId, cropType } = await readBody(event); // 1. --- Validation --- if (typeof fieldId !== 'number' || !cropType) { throw createError({ statusCode: 400, statusMessage: 'Invalid request body. "fieldId" and "cropType" are required.' }); } if (!Object.values(CropType).includes(cropType)) { throw createError({ statusCode: 400, statusMessage: 'Invalid crop type.' }); } // 2. --- Find the target field and validate its state --- const field = await prisma.villageObject.findFirst({ where: { id: fieldId, type: 'FIELD', village: { userId: userId }, // Ensures ownership }, }); if (!field) { throw createError({ statusCode: 404, statusMessage: 'Field not found or you do not own it.' }); } if (field.cropType !== null) { throw createError({ statusCode: 409, statusMessage: 'A crop is already planted in this field.' }); } // 3. --- Perform atomic transaction --- try { const [, updatedField] = await prisma.$transaction([ prisma.user.update({ where: { id: userId, coins: { gte: PLANTING_COST } }, data: { coins: { decrement: PLANTING_COST } }, }), prisma.villageObject.update({ where: { id: fieldId }, data: { cropType: cropType, plantedAt: new Date(), }, }), ]); return { id: updatedField.id, type: updatedField.type, x: updatedField.x, y: updatedField.y, cropType: updatedField.cropType, isGrown: isCropGrown(updatedField.plantedAt, updatedField.cropType), // will be false }; } catch (e) { throw createError({ statusCode: 402, statusMessage: 'Insufficient coins to plant seeds.' }); } });