'use server'; import { redirect } from "next/navigation"; import { prisma } from "@/lib/prisma"; import { hashPassword, verifyPassword, generateToken } from "@/lib/crypto"; import { createSession, deleteSession, requireCurrentUser, } from "@/lib/auth"; import { Resend } from "resend"; import type { FormState } from "@/lib/types"; export async function login( prevState: FormState | undefined, formData: FormData, ): Promise { const email = formData.get("email") as string; const password = formData.get("password") as string; if (!email || !password) { return { message: "Email and password are required" }; } const user = await prisma.user.findUnique({ where: { email } }); if (!user) { return { message: "Invalid email or password" }; } const valid = await verifyPassword(password, user.passwordHash); if (!valid) { return { message: "Invalid email or password" }; } await createSession(user.id); redirect("/"); } export async function logout(): Promise { await deleteSession(); redirect("/login"); } export async function changePassword( prevState: FormState | undefined, formData: FormData, ): Promise { const currentUser = await requireCurrentUser(); const currentPassword = formData.get("currentPassword") as string; const newPassword = formData.get("newPassword") as string; const confirmPassword = formData.get("confirmPassword") as string; if (!currentPassword || !newPassword || !confirmPassword) { return { message: "All fields are required" }; } if (newPassword.length < 8) { return { errors: { newPassword: ["Password must be at least 8 characters"] }, message: "Password must be at least 8 characters", }; } if (newPassword !== confirmPassword) { return { errors: { confirmPassword: ["Passwords do not match"] }, message: "Passwords do not match", }; } const user = await prisma.user.findUnique({ where: { id: currentUser.id }, }); if (!user) { return { message: "User not found" }; } const valid = await verifyPassword(currentPassword, user.passwordHash); if (!valid) { return { errors: { currentPassword: ["Current password is incorrect"] }, message: "Current password is incorrect", }; } const newHash = await hashPassword(newPassword); await prisma.user.update({ where: { id: user.id }, data: { passwordHash: newHash }, }); return { message: "Password changed successfully", success: true }; } export async function forgotPassword( prevState: FormState | undefined, formData: FormData, ): Promise { const email = formData.get("email") as string; if (!email) { return { message: "Email is required" }; } const user = await prisma.user.findUnique({ where: { email } }); if (user) { const token = generateToken(); await prisma.passwordResetToken.create({ data: { token, userId: user.id, expiresAt: new Date(Date.now() + 60 * 60 * 1000), }, }); const resetLink = `${process.env.NEXT_PUBLIC_APP_URL}/reset-password?token=${token}`; if (process.env.RESEND_API_KEY) { try { const resend = new Resend(process.env.RESEND_API_KEY); await resend.emails.send({ from: "Order Loop ", to: email, subject: "Reset your password", html: `

Click here to reset your password. This link expires in 1 hour.

`, }); } catch (error) { console.error("Failed to send reset email:", error); console.log("Password reset link:", resetLink); } } else { console.log("Password reset link:", resetLink); } } return { message: "If an account exists, a reset link has been sent", success: true, }; } export async function resetPassword( prevState: FormState | undefined, formData: FormData, ): Promise { const token = formData.get("token") as string; const password = formData.get("password") as string; const confirmPassword = formData.get("confirmPassword") as string; if (!token || !password || !confirmPassword) { return { message: "All fields are required" }; } if (password.length < 8) { return { errors: { password: ["Password must be at least 8 characters"] }, message: "Password must be at least 8 characters", }; } if (password !== confirmPassword) { return { errors: { confirmPassword: ["Passwords do not match"] }, message: "Passwords do not match", }; } const resetToken = await prisma.passwordResetToken.findUnique({ where: { token }, }); if (!resetToken || resetToken.used || resetToken.expiresAt < new Date()) { return { message: "Invalid or expired reset token" }; } const newHash = await hashPassword(password); await prisma.user.update({ where: { id: resetToken.userId }, data: { passwordHash: newHash }, }); await prisma.passwordResetToken.update({ where: { id: resetToken.id }, data: { used: true }, }); return { message: "Password reset successfully. You can now log in.", success: true, }; }