43 lines
1.5 KiB
TypeScript
43 lines
1.5 KiB
TypeScript
// pages/api/login.ts
|
|
import type { NextApiRequest, NextApiResponse } from "next";
|
|
import { PrismaClient } from "@prisma/client";
|
|
import bcrypt from "bcrypt";
|
|
import jwt from "jsonwebtoken";
|
|
|
|
const prisma = new PrismaClient();
|
|
const SECRET_KEY = process.env.JWT_SECRET as string;
|
|
|
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
if (req.method !== "POST") return res.status(405).json({ message: "Method not allowed" });
|
|
|
|
try {
|
|
const { email, password } = req.body as { email?: string; password?: string };
|
|
if (!email || !password) return res.status(400).json({ message: "Email and password are required" });
|
|
|
|
const user = await prisma.user.findUnique({ where: { email } });
|
|
if (!user) return res.status(401).json({ message: "Invalid credentials" });
|
|
|
|
const isMatch = await bcrypt.compare(password, user.password);
|
|
if (!isMatch) return res.status(401).json({ message: "Invalid credentials" });
|
|
|
|
const token = jwt.sign({ sub: user.id, email: user.email }, SECRET_KEY, { expiresIn: "1d" });
|
|
|
|
const isProd = process.env.NODE_ENV === "production";
|
|
const cookie = [
|
|
`token=${token}`,
|
|
"HttpOnly",
|
|
"Path=/",
|
|
"SameSite=Strict",
|
|
`Max-Age=${60 * 60 * 24}`, // 1 day
|
|
isProd ? "Secure" : "", // only secure in prod
|
|
].filter(Boolean).join("; ");
|
|
|
|
res.setHeader("Set-Cookie", cookie);
|
|
return res.status(200).json({ message: "Login successful" });
|
|
} catch (e) {
|
|
console.error(e);
|
|
return res.status(500).json({ message: "Something went wrong" });
|
|
}
|
|
}
|
|
|