129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
import { createCookieSessionStorage, redirect } from "@remix-run/node";
|
|
import { PrismaClient } from "@prisma/client";
|
|
import bcrypt from "bcryptjs";
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
// 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 },
|
|
});
|
|
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;
|
|
}
|
|
|
|
return { id: user.id, username: user.username, email: user.email, name: user.name, authLevel: user.authLevel };
|
|
}
|
|
|
|
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,
|
|
},
|
|
});
|
|
|
|
return { id: user.id, username: user.username, email: user.email, name: user.name, authLevel: user.authLevel };
|
|
} |