"use server"; import { getDb } from "@/lib/mongodb"; import { getLoggedInUser, logoutUser } from "@/lib/actions/auth"; import bcrypt from 'bcryptjs'; import { differenceInHours } from 'date-fns'; import type { User, Deployment } from "@/lib/types"; import type { TransferCoinsInput, ChangePasswordInput, UpdateProfileInput, DeleteAccountInput } from "@/lib/schemas"; import { revalidatePath } from 'next/cache'; const DAILY_COIN_AMOUNT = 10; const CLAIM_COOLDOWN_HOURS = 24; export async function claimDailyCoins(): Promise<{ success: boolean; message: string; newBalance?: number; nextClaimAvailableInMs?: number }> { const user = await getLoggedInUser(); if (!user) { return { success: false, message: "You must be logged in to claim coins." }; } const now = new Date(); if (user.lastCoinClaim) { const hoursSinceLastClaim = differenceInHours(now, new Date(user.lastCoinClaim)); if (hoursSinceLastClaim < CLAIM_COOLDOWN_HOURS) { const nextClaimTime = new Date(user.lastCoinClaim).getTime() + CLAIM_COOLDOWN_HOURS * 60 * 60 * 1000; const nextClaimAvailableInMs = Math.max(0, nextClaimTime - now.getTime()); return { success: false, message: `You can claim again in ${CLAIM_COOLDOWN_HOURS - hoursSinceLastClaim} hour(s).`, nextClaimAvailableInMs }; } } try { const db = await getDb(); const usersCollection = db.collection("users"); // Query by string _id directly const updatedUserResult = await usersCollection.findOneAndUpdate( { _id: user._id }, { $inc: { coins: DAILY_COIN_AMOUNT }, $set: { lastCoinClaim: now } }, { returnDocument: "after" } ); if (!updatedUserResult) { return { success: false, message: "Failed to update user balance. User not found or update failed." }; } revalidatePath("/dashboard"); return { success: true, message: `Successfully claimed ${DAILY_COIN_AMOUNT} coins!`, newBalance: updatedUserResult.coins, nextClaimAvailableInMs: CLAIM_COOLDOWN_HOURS * 60 * 60 * 1000 }; } catch (error) { console.error("Error claiming daily coins:", error); return { success: false, message: "An unexpected error occurred." }; } } export async function transferCoins(data: TransferCoinsInput): Promise<{ success: boolean; message: string; newSenderBalance?: number }> { const sender = await getLoggedInUser(); if (!sender) { return { success: false, message: "You must be logged in to transfer coins." }; } if (data.amount <= 0) { return { success: false, message: "Transfer amount must be positive." }; } if (sender.coins < data.amount && sender.role !== 'admin') { return { success: false, message: "Insufficient coin balance." }; } if (sender.email === data.recipientEmail) { return { success: false, message: "You cannot transfer coins to yourself." }; } try { const db = await getDb(); const usersCollection = db.collection("users"); const recipient = await usersCollection.findOne({ email: data.recipientEmail }); if (!recipient) { return { success: false, message: "Recipient user not found." }; } if (sender.role !== 'admin') { // Query by string _id directly const freshSenderData = await usersCollection.findOne({ _id: sender._id }); if (!freshSenderData || freshSenderData.coins < data.amount) { return { success: false, message: "Insufficient coin balance. Please refresh and try again." }; } } const senderUpdateResult = await usersCollection.updateOne( { _id: sender._id }, // Query by string _id { $inc: { coins: -data.amount } } ); if (senderUpdateResult.modifiedCount === 0) { if (sender.role !== 'admin') { return { success: false, message: "Failed to update your balance. Please try again." }; } } const recipientUpdateResult = await usersCollection.updateOne( { _id: recipient._id }, // Query by string _id { $inc: { coins: data.amount } } ); if (recipientUpdateResult.modifiedCount === 0) { // Rollback sender's coins if recipient update failed await usersCollection.updateOne( { _id: sender._id }, // Query by string _id { $inc: { coins: data.amount } } ); return { success: false, message: "Failed to transfer coins to recipient. Your balance has been restored." }; } const newSenderData = await usersCollection.findOne({ _id: sender._id }); // Query by string _id revalidatePath("/dashboard"); return { success: true, message: `Successfully transferred ${data.amount} coins to ${recipient.name}.`, newSenderBalance: newSenderData?.coins ?? sender.coins - data.amount, }; } catch (error) { console.error("Error transferring coins:", error); return { success: false, message: "An unexpected error occurred during the transfer." }; } } export async function updateProfile(data: UpdateProfileInput): Promise<{ success: boolean; message: string }> { const loggedInUser = await getLoggedInUser(); if (!loggedInUser) { return { success: false, message: "You must be logged in to update your profile." }; } try { const db = await getDb(); const usersCollection = db.collection("users"); const result = await usersCollection.updateOne( { _id: loggedInUser._id }, // Query by string _id { $set: { name: data.name } } ); if (result.modifiedCount === 0 && result.matchedCount > 0) { return { success: true, message: "No changes detected in profile information." }; } if (result.modifiedCount === 0) { return { success: false, message: "Failed to update profile. User not found." }; } revalidatePath('/dashboard/profile'); revalidatePath('/dashboard'); return { success: true, message: "Profile updated successfully." }; } catch (error) { console.error("Error updating profile:", error); return { success: false, message: "An unexpected error occurred while updating profile." }; } } export async function changePassword(data: ChangePasswordInput): Promise<{ success: boolean; message: string }> { const loggedInUser = await getLoggedInUser(); if (!loggedInUser) { return { success: false, message: "You must be logged in to change your password." }; } try { const db = await getDb(); const usersCollection = db.collection("users"); const userFromDb = await usersCollection.findOne({ _id: loggedInUser._id }); // Query by string _id if (!userFromDb) { return { success: false, message: "User not found." }; } const isCurrentPasswordValid = await bcrypt.compare(data.currentPassword, userFromDb.passwordHash); if (!isCurrentPasswordValid) { return { success: false, message: "Incorrect current password." }; } const newPasswordHash = await bcrypt.hash(data.newPassword, 10); await usersCollection.updateOne( { _id: loggedInUser._id }, // Query by string _id { $set: { passwordHash: newPasswordHash } } ); return { success: true, message: "Password changed successfully." }; } catch (error) { console.error("Error changing password:", error); return { success: false, message: "An unexpected error occurred while changing password." }; } } export async function deleteUserAccount(data: DeleteAccountInput): Promise<{ success: boolean; message: string }> { const loggedInUser = await getLoggedInUser(); if (!loggedInUser) { return { success: false, message: "You must be logged in to delete your account." }; } try { const db = await getDb(); const usersCollection = db.collection("users"); const deploymentsCollection = db.collection("deployments"); const userFromDb = await usersCollection.findOne({ _id: loggedInUser._id }); // Query by string _id if (!userFromDb) { return { success: false, message: "User not found." }; } const isCurrentPasswordValid = await bcrypt.compare(data.currentPassword, userFromDb.passwordHash); if (!isCurrentPasswordValid) { return { success: false, message: "Incorrect password. Account deletion failed." }; } if (userFromDb.role === 'admin') { const adminCount = await usersCollection.countDocuments({ role: 'admin' }); if (adminCount <= 1) { return { success: false, message: "Cannot delete the last admin account. Promote another user to admin first." }; } } await deploymentsCollection.deleteMany({ userId: loggedInUser._id }); // Query by string _id const deleteResult = await usersCollection.deleteOne({ _id: loggedInUser._id }); // Query by string _id if (deleteResult.deletedCount === 0) { return { success: false, message: "Failed to delete user account from the database." }; } await logoutUser(); return { success: true, message: "Account and associated deployments deleted successfully. You have been logged out." }; } catch (error) { console.error("Error deleting account:", error); if (error instanceof Error && error.message.includes('NEXT_REDIRECT')) { throw error; } return { success: false, message: "An unexpected error occurred while deleting your account." }; } }