habits.andr33v.ru/server/api/village/objects/[id].patch.ts

74 lines
2.7 KiB
TypeScript

import { getUserIdFromSession } from '../../../utils/auth';
import { VILLAGE_GRID_SIZE, MOVE_COST, isCropGrown } from '../../../utils/village';
// --- Handler ---
export default defineEventHandler(async (event) => {
const userId = await getUserIdFromSession(event);
const objectId = parseInt(event.context.params?.id || '', 10);
const { x, y } = await readBody(event);
// 1. --- Validation ---
if (isNaN(objectId)) {
throw createError({ statusCode: 400, statusMessage: 'Invalid object ID.' });
}
if (typeof x !== 'number' || typeof y !== 'number') {
throw createError({ statusCode: 400, statusMessage: 'Invalid request body. "x" and "y" are required.' });
}
if (x < 0 || x >= VILLAGE_GRID_SIZE.width || y < 0 || y >= VILLAGE_GRID_SIZE.height) {
throw createError({ statusCode: 400, statusMessage: 'Cannot move object outside of village bounds.' });
}
// 2. --- Fetch state and enforce rules ---
const village = await prisma.village.findUnique({
where: { userId },
include: { objects: true },
});
if (!village) {
throw createError({ statusCode: 404, statusMessage: 'Village not found.' });
}
const objectToMove = village.objects.find(obj => obj.id === objectId);
// Rule: Object must exist and belong to the user (implicit via village)
if (!objectToMove) {
throw createError({ statusCode: 404, statusMessage: 'Object not found.' });
}
// Rule: Cannot move obstacles
if (objectToMove.type === 'OBSTACLE') {
throw createError({ statusCode: 400, statusMessage: 'Cannot move obstacles. They must be cleared.' });
}
// Rule: Target cell must be empty (and not the same cell)
if (village.objects.some(obj => obj.x === x && obj.y === y)) {
throw createError({ statusCode: 409, statusMessage: 'Target cell is already occupied.' });
}
// 3. --- Perform atomic transaction ---
try {
const [, updatedObject] = await prisma.$transaction([
prisma.user.update({
where: { id: userId, coins: { gte: MOVE_COST } },
data: { coins: { decrement: MOVE_COST } },
}),
prisma.villageObject.update({
where: { id: objectId },
data: { x, y },
}),
]);
return {
id: updatedObject.id,
type: updatedObject.type,
x: updatedObject.x,
y: updatedObject.y,
cropType: updatedObject.cropType,
isGrown: isCropGrown(updatedObject.plantedAt, updatedObject.cropType),
};
} catch (e) {
throw createError({ statusCode: 402, statusMessage: 'Insufficient coins to move object.' });
}
});