habits.andr33v.ru/server/api/village/plant.post.ts

64 lines
2.2 KiB
TypeScript

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.' });
}
});