habits.andr33v.ru/server/api/quests/daily-visit.post.ts

107 lines
2.5 KiB
TypeScript

import { useSession } from 'h3';
interface DailyVisitResponse {
message: string;
reward: {
coins: number;
streakBonus: boolean;
};
updatedCoins: number;
}
/**
* A helper function to safely get the authenticated user's ID from the session.
*/
async function getUserIdFromSession(event: any): Promise<number> {
const session = await useSession(event, {
password: process.env.SESSION_PASSWORD,
});
const userId = session.data?.user?.id;
if (!userId) {
throw createError({ statusCode: 401, statusMessage: 'Unauthorized' });
}
return userId;
}
/**
* Creates a Date object for the start of a given day in UTC.
*/
function getStartOfDay(date: Date): Date {
const startOfDay = new Date(date);
startOfDay.setUTCHours(0, 0, 0, 0);
return startOfDay;
}
export default defineEventHandler(async (event): Promise<DailyVisitResponse> => {
const userId = await getUserIdFromSession(event);
const today = getStartOfDay(new Date());
// 1. Check if the user has already claimed the reward today
const existingVisit = await prisma.dailyVisit.findUnique({
where: {
userId_date: {
userId,
date: today,
},
},
});
if (existingVisit) {
throw createError({
statusCode: 409,
statusMessage: 'Daily visit reward has already been claimed today.',
});
}
// 2. Check for a 5-day consecutive streak (i.e., visits on the 4 previous days)
const previousDates = Array.from({ length: 4 }, (_, i) => {
const d = new Date(today);
d.setUTCDate(d.getUTCDate() - (i + 1));
return d;
});
const priorVisitsCount = await prisma.dailyVisit.count({
where: {
userId,
date: {
in: previousDates,
},
},
});
const hasStreak = priorVisitsCount === 4;
// 3. Calculate rewards and update the database in a transaction
let totalReward = 1; // Base reward
if (hasStreak) {
totalReward += 10; // Streak bonus
}
const [, updatedUser] = await prisma.$transaction([
prisma.dailyVisit.create({
data: {
userId,
date: today,
},
}),
prisma.user.update({
where: { id: userId },
data: {
coins: {
increment: totalReward,
},
},
}),
]);
// 4. Return the response
return {
message: hasStreak ? 'Daily visit and streak bonus claimed!' : 'Daily visit claimed!',
reward: {
coins: totalReward,
streakBonus: hasStreak,
},
updatedCoins: updatedUser.coins,
};
});