import { createCookieSessionStorage, redirect } from "@remix-run/node"; import bcrypt from "bcryptjs"; import { prisma } from "~/utils/db.server"; // Session storage const sessionSecret = process.env.SESSION_SECRET || "default-secret"; const { getSession, commitSession, destroySession } = createCookieSessionStorage({ cookie: { name: "__session", httpOnly: true, maxAge: 60 * 60 * 24 * 7, // 7 days path: "/", sameSite: "lax", secrets: [sessionSecret], secure: process.env.NODE_ENV === "production", }, }); export { getSession, commitSession, destroySession }; // Auth functions export async function createUserSession(userId: number, redirectTo: string) { const session = await getSession(); session.set("userId", userId); return redirect(redirectTo, { headers: { "Set-Cookie": await commitSession(session), }, }); } export async function getUserSession(request: Request) { return getSession(request.headers.get("Cookie")); } export async function getUserId(request: Request) { const session = await getUserSession(request); const userId = session.get("userId"); if (!userId || typeof userId !== "number") return null; return userId; } export async function requireUserId(request: Request, redirectTo: string = "/signin") { const userId = await getUserId(request); if (!userId) { const searchParams = new URLSearchParams([["redirectTo", new URL(request.url).pathname]]); throw redirect(`${redirectTo}?${searchParams}`); } return userId; } export async function getUser(request: Request) { const userId = await getUserId(request); if (!userId) return null; try { const user = await prisma.employee.findUnique({ where: { id: userId }, select: { id: true, name: true, username: true, email: true, authLevel: true, status: true }, }); // If user is inactive, logout and return null if (user && user.status === 'inactive') { throw logout(request); } return user; } catch { throw logout(request); } } export async function requireUser(request: Request) { const user = await getUser(request); if (!user) { throw redirect("/signin"); } return user; } export async function requireAuthLevel(request: Request, minLevel: number = 2) { const user = await requireUser(request); if (user.authLevel < minLevel) { throw redirect("/signin"); //throw new Response("Unauthorized", { status: 403 }); // redirect to dashboard page //redirect("/dashboard"); } return user; } export async function logout(request: Request) { const session = await getUserSession(request); return redirect("/signin", { headers: { "Set-Cookie": await destroySession(session), }, }); } export async function verifyLogin(usernameOrEmail: string, password: string) { // Try to find user by username first, then by email const user = await prisma.employee.findFirst({ where: { OR: [ { username: usernameOrEmail }, { email: usernameOrEmail } ] } }); if (!user || !bcrypt.compareSync(password, user.password)) { return null; } // Check if user is inactive if (user.status === 'inactive') { return null; } return { id: user.id, username: user.username, email: user.email, name: user.name, authLevel: user.authLevel, status: user.status }; } export async function createUser(name: string, username: string, email: string, password: string, authLevel: number = 1) { const hashedPassword = bcrypt.hashSync(password, 10); const user = await prisma.employee.create({ data: { name, username, email, password: hashedPassword, authLevel, status: 'active', }, }); return { id: user.id, username: user.username, email: user.email, name: user.name, authLevel: user.authLevel, status: user.status }; }