File size: 9,342 Bytes
e9d5b7d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

"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<User>("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<User>("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<User>("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<User>("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<User>("users");
    const deploymentsCollection = db.collection<Deployment>("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." };
  }
}