"use server"; import { prisma } from "@/lib/prisma"; import bcrypt from "bcryptjs"; import { cookies } from "next/headers"; import { SignJWT, jwtVerify } from "jose"; const getSecretKey = () => { const s = process.env.SESSION_SECRET; if (!s || s.length < 32) { throw new Error("SESSION_SECRET environment variable is required (min 32 chars)."); } return new TextEncoder().encode(s); }; export async function registerClientRequest(formData: FormData) { const fullName = formData.get("fullName") as string; const email = formData.get("email") as string; const companyName = formData.get("companyName") as string; const password = formData.get("password") as string; if (!fullName || !email || !companyName || !password) return { error: "All fields are required." }; try { const existing = await prisma.clientUser.findUnique({ where: { email: email.toLowerCase().trim() } }); if (existing) return { error: "Email is already registered." }; const passwordHash = await bcrypt.hash(password, 12); const client = await prisma.clientUser.create({ data: { email: email.toLowerCase().trim(), passwordHash, fullName, companyName, isApproved: false } }); const year = new Date().getFullYear(); const count = await prisma.operationsSignal.count({ where: { ticketId: { startsWith: `ACC-${year}` } } }); const seq = String(count + 1).padStart(4, "0"); await prisma.operationsSignal.create({ data: { ticketId: `ACC-${year}-${seq}`, type: "ACCESS_REQUEST", status: "PENDING", clientName: fullName, clientEmail: email, clientCompany: companyName, clientId: client.id, message: `New B2B portal access request from ${companyName}. Please verify their credentials before approving.`, } }); return { success: true }; } catch (error) { return { error: "An error occurred during registration." }; } } export async function loginClient(formData: FormData) { const email = formData.get("email") as string; const password = formData.get("password") as string; if (!email || !password) return { error: "Email and password are required." }; try { const user = await prisma.clientUser.findUnique({ where: { email: email.toLowerCase().trim() } }); if (!user) return { error: "Invalid credentials." }; if (!user.isApproved) return { error: "Your account is still pending engineering approval." }; const isValid = await bcrypt.compare(password, user.passwordHash); if (!isValid) return { error: "Invalid credentials." }; await prisma.clientUser.update({ where: { id: user.id }, data: { lastLoginAt: new Date() } }); const token = await new SignJWT({ userId: user.id, email: user.email, name: user.fullName, company: user.companyName }) .setProtectedHeader({ alg: 'HS256' }) .setExpirationTime('7d') .sign(getSecretKey()); const cookieStore = await cookies(); cookieStore.set("flux_b2b_session", token, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax", maxAge: 60 * 60 * 24 * 7, path: "/", }); return { success: true }; } catch (error) { return { error: "Login failed." }; } } export async function logoutClient() { const cookieStore = await cookies(); cookieStore.delete("flux_b2b_session"); return { success: true }; } export async function getClientSession() { const cookieStore = await cookies(); const token = cookieStore.get("flux_b2b_session")?.value; if (!token) return null; try { const { payload } = await jwtVerify(token, getSecretKey()); return payload as { userId: string; email: string; name: string; company: string }; } catch (error) { return null; } } export async function updateClientPassword(formData: FormData) { const session = await getClientSession(); if (!session) return { error: "Unauthorized." }; const currentPassword = formData.get("currentPassword") as string; const newPassword = formData.get("newPassword") as string; try { const user = await prisma.clientUser.findUnique({ where: { id: session.userId } }); if (!user) return { error: "User not found." }; const isValid = await bcrypt.compare(currentPassword, user.passwordHash); if (!isValid) return { error: "Current password is incorrect." }; const newHash = await bcrypt.hash(newPassword, 12); await prisma.clientUser.update({ where: { id: user.id }, data: { passwordHash: newHash } }); return { success: true }; } catch (error) { return { error: "Failed to update password." }; } }