178 lines
No EOL
6.1 KiB
TypeScript
178 lines
No EOL
6.1 KiB
TypeScript
import { auth } from '$lib/auth';
|
|
import { error, json } from '@sveltejs/kit';
|
|
import { db } from '$lib/server/db';
|
|
import { user } from '$lib/server/db/schema';
|
|
import { eq } from 'drizzle-orm';
|
|
import type { RequestHandler } from './$types';
|
|
|
|
const TWELVE_HOURS_MS = 12 * 60 * 60 * 1000;
|
|
const THIRTY_SIX_HOURS_MS = 36 * 60 * 60 * 1000;
|
|
|
|
const REWARD_TIERS = [
|
|
1200, // Day 1
|
|
1500, // Day 2
|
|
1800, // Day 3
|
|
2100, // Day 4
|
|
2500, // Day 5
|
|
3000, // Day 6
|
|
3500, // Day 7
|
|
4000, // Day 8+
|
|
];
|
|
|
|
const PRESTIGE_MULTIPLIERS = {
|
|
0: 1.0, // No prestige
|
|
1: 1.25, // 25% bonus
|
|
2: 1.5, // 50% bonus
|
|
3: 1.75, // 75% bonus
|
|
4: 2.0, // 100% bonus
|
|
5: 2.5, // 150% bonus
|
|
};
|
|
|
|
function getPrestigeMultiplier(prestigeLevel: number): number {
|
|
return PRESTIGE_MULTIPLIERS[prestigeLevel as keyof typeof PRESTIGE_MULTIPLIERS] || 1.0;
|
|
}
|
|
|
|
function calculateStreak(lastClaim: Date | null, currentStreak: number): number {
|
|
if (!lastClaim) return 1;
|
|
|
|
const timeSinceLastClaim = Date.now() - lastClaim.getTime();
|
|
|
|
// reset streak if more than 36 hours
|
|
if (timeSinceLastClaim > THIRTY_SIX_HOURS_MS) return 1;
|
|
|
|
// only increment if within valid window (12-36 hours)
|
|
if (timeSinceLastClaim >= TWELVE_HOURS_MS) return currentStreak + 1;
|
|
|
|
return currentStreak;
|
|
}
|
|
|
|
function calculateReward(streak: number, prestigeLevel: number = 0): { total: number; base: number; prestigeBonus: number } {
|
|
const tierIndex = Math.min(streak - 1, REWARD_TIERS.length - 1);
|
|
const base = REWARD_TIERS[tierIndex];
|
|
const prestigeMultiplier = getPrestigeMultiplier(prestigeLevel);
|
|
const prestigeBonus = base * (prestigeMultiplier - 1);
|
|
const total = base + prestigeBonus;
|
|
|
|
return { total, base, prestigeBonus };
|
|
}
|
|
|
|
export const POST: RequestHandler = async ({ request }) => {
|
|
const session = await auth.api.getSession({ headers: request.headers });
|
|
if (!session?.user) throw error(401, 'Not authenticated');
|
|
|
|
const userId = Number(session.user.id);
|
|
const now = new Date();
|
|
|
|
return await db.transaction(async (tx) => {
|
|
const userData = await tx.select({
|
|
id: user.id,
|
|
baseCurrencyBalance: user.baseCurrencyBalance,
|
|
lastRewardClaim: user.lastRewardClaim,
|
|
totalRewardsClaimed: user.totalRewardsClaimed,
|
|
loginStreak: user.loginStreak,
|
|
prestigeLevel: user.prestigeLevel
|
|
})
|
|
.from(user)
|
|
.where(eq(user.id, userId))
|
|
.for('update')
|
|
.limit(1);
|
|
|
|
if (!userData[0]) {
|
|
throw error(404, 'User not found');
|
|
}
|
|
|
|
const currentUser = userData[0];
|
|
|
|
if (currentUser.lastRewardClaim) {
|
|
const timeSinceLastClaim = now.getTime() - currentUser.lastRewardClaim.getTime();
|
|
if (timeSinceLastClaim < TWELVE_HOURS_MS) {
|
|
return json({
|
|
error: 'Daily reward not yet available',
|
|
canClaim: false,
|
|
timeRemaining: TWELVE_HOURS_MS - timeSinceLastClaim
|
|
}, { status: 429 });
|
|
}
|
|
}
|
|
|
|
const newStreak = calculateStreak(currentUser.lastRewardClaim, currentUser.loginStreak || 0);
|
|
const reward = calculateReward(newStreak, currentUser.prestigeLevel || 0);
|
|
|
|
const currentBalance = parseFloat(currentUser.baseCurrencyBalance || '0');
|
|
const currentTotalRewards = parseFloat(currentUser.totalRewardsClaimed || '0');
|
|
const newBalance = currentBalance + reward.total;
|
|
const newTotalRewards = currentTotalRewards + reward.total;
|
|
|
|
await tx.update(user)
|
|
.set({
|
|
baseCurrencyBalance: newBalance.toFixed(8),
|
|
lastRewardClaim: now,
|
|
totalRewardsClaimed: newTotalRewards.toFixed(8),
|
|
loginStreak: newStreak
|
|
})
|
|
.where(eq(user.id, currentUser.id));
|
|
|
|
return json({
|
|
success: true,
|
|
rewardAmount: reward.total,
|
|
baseReward: reward.base,
|
|
prestigeBonus: reward.prestigeBonus,
|
|
prestigeLevel: currentUser.prestigeLevel || 0,
|
|
newBalance,
|
|
totalRewardsClaimed: newTotalRewards,
|
|
loginStreak: newStreak,
|
|
nextClaimTime: new Date(now.getTime() + TWELVE_HOURS_MS)
|
|
});
|
|
});
|
|
};
|
|
|
|
export const GET: RequestHandler = async ({ request }) => {
|
|
const session = await auth.api.getSession({ headers: request.headers });
|
|
if (!session?.user) throw error(401, 'Not authenticated');
|
|
|
|
const [currentUser] = await db.select({
|
|
id: user.id,
|
|
baseCurrencyBalance: user.baseCurrencyBalance,
|
|
lastRewardClaim: user.lastRewardClaim,
|
|
totalRewardsClaimed: user.totalRewardsClaimed,
|
|
loginStreak: user.loginStreak,
|
|
prestigeLevel: user.prestigeLevel
|
|
})
|
|
.from(user)
|
|
.where(eq(user.id, Number(session.user.id)))
|
|
.limit(1);
|
|
|
|
if (!currentUser) {
|
|
throw error(404, 'User not found');
|
|
}
|
|
|
|
const now = new Date();
|
|
|
|
let canClaim = true;
|
|
let timeRemaining = 0;
|
|
let nextClaimTime = null;
|
|
|
|
if (currentUser.lastRewardClaim) {
|
|
const timeSinceLastClaim = now.getTime() - currentUser.lastRewardClaim.getTime();
|
|
if (timeSinceLastClaim < TWELVE_HOURS_MS) {
|
|
canClaim = false;
|
|
timeRemaining = TWELVE_HOURS_MS - timeSinceLastClaim;
|
|
nextClaimTime = new Date(currentUser.lastRewardClaim.getTime() + TWELVE_HOURS_MS);
|
|
}
|
|
}
|
|
|
|
const potentialStreak = calculateStreak(currentUser.lastRewardClaim, currentUser.loginStreak || 0);
|
|
const reward = calculateReward(potentialStreak, currentUser.prestigeLevel || 0);
|
|
|
|
return json({
|
|
canClaim,
|
|
rewardAmount: reward.total,
|
|
baseReward: reward.base,
|
|
prestigeBonus: reward.prestigeBonus,
|
|
prestigeLevel: currentUser.prestigeLevel || 0,
|
|
timeRemaining,
|
|
nextClaimTime,
|
|
totalRewardsClaimed: Number(currentUser.totalRewardsClaimed || 0),
|
|
lastRewardClaim: currentUser.lastRewardClaim,
|
|
loginStreak: currentUser.loginStreak || 0
|
|
});
|
|
}; |