feature/syasya/testlayout #8
@ -1,46 +0,0 @@
 | 
			
		||||
// pages/api/auth/me.ts
 | 
			
		||||
import type { NextApiRequest, NextApiResponse } from "next";
 | 
			
		||||
import jwt, { JwtPayload } from "jsonwebtoken";
 | 
			
		||||
import { PrismaClient } from "@prisma/client";
 | 
			
		||||
 | 
			
		||||
const prisma = new PrismaClient();
 | 
			
		||||
const SECRET_KEY = process.env.JWT_SECRET as string;
 | 
			
		||||
 | 
			
		||||
function readCookieToken(req: NextApiRequest) {
 | 
			
		||||
  const cookie = req.headers.cookie || "";
 | 
			
		||||
  const match = cookie.split("; ").find((c) => c.startsWith("token="));
 | 
			
		||||
  return match?.split("=")[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function readAuthBearer(req: NextApiRequest) {
 | 
			
		||||
  const auth = req.headers.authorization;
 | 
			
		||||
  if (!auth?.startsWith("Bearer ")) return undefined;
 | 
			
		||||
  return auth.slice("Bearer ".length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function hasEmail(payload: string | JwtPayload): payload is JwtPayload & { email: string } {
 | 
			
		||||
  return typeof payload === "object" && payload !== null && typeof (payload as any).email === "string";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
			
		||||
  if (req.method !== "GET") return res.status(405).json({ message: "Method not allowed" });
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const token = readAuthBearer(req) ?? readCookieToken(req);
 | 
			
		||||
    if (!token) return res.status(401).json({ message: "Unauthorized" });
 | 
			
		||||
 | 
			
		||||
    const decoded = jwt.verify(token, SECRET_KEY);
 | 
			
		||||
    if (!hasEmail(decoded)) return res.status(401).json({ message: "Invalid token" });
 | 
			
		||||
 | 
			
		||||
    const user = await prisma.user.findUnique({
 | 
			
		||||
      where: { email: decoded.email },
 | 
			
		||||
      select: { id: true, email: true, createdAt: true },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!user) return res.status(401).json({ message: "User not found" });
 | 
			
		||||
    return res.status(200).json({ user });
 | 
			
		||||
  } catch {
 | 
			
		||||
    return res.status(401).json({ message: "Invalid token" });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,42 +0,0 @@
 | 
			
		||||
// 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: String(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" });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,22 +0,0 @@
 | 
			
		||||
// pages/api/logout.ts  ->  /api/logout
 | 
			
		||||
import type { NextApiRequest, NextApiResponse } from 'next';
 | 
			
		||||
import { serialize } from 'cookie';
 | 
			
		||||
 | 
			
		||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
			
		||||
  const isProd = process.env.NODE_ENV === 'production';
 | 
			
		||||
 | 
			
		||||
  const setCookie = serialize('token', '', {
 | 
			
		||||
    httpOnly: true,
 | 
			
		||||
    secure: isProd,
 | 
			
		||||
    sameSite: 'strict',  // matches login
 | 
			
		||||
    path: '/',           // matches login
 | 
			
		||||
    maxAge: 0,
 | 
			
		||||
    expires: new Date(0),
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  res.setHeader('Set-Cookie', setCookie);
 | 
			
		||||
  res.setHeader('Cache-Control', 'no-store');
 | 
			
		||||
  return res.status(200).json({ message: 'Logged out' });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,42 +0,0 @@
 | 
			
		||||
// pages/api/register.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 existingUser = await prisma.user.findUnique({ where: { email } });
 | 
			
		||||
    if (existingUser) return res.status(400).json({ message: "User already exists" });
 | 
			
		||||
 | 
			
		||||
    const hashedPassword = await bcrypt.hash(password, 10);
 | 
			
		||||
    const user = await prisma.user.create({
 | 
			
		||||
      data: { email, password: hashedPassword },
 | 
			
		||||
      select: { id: true, email: true, createdAt: true }, // do NOT expose password
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const token = jwt.sign({ sub: String(user.id), email: user.email }, SECRET_KEY, { expiresIn: "1d" });
 | 
			
		||||
 | 
			
		||||
    // Set a secure, httpOnly cookie
 | 
			
		||||
    const maxAge = 60 * 60 * 24; // 1 day
 | 
			
		||||
    res.setHeader(
 | 
			
		||||
      "Set-Cookie",
 | 
			
		||||
      `token=${token}; HttpOnly; Path=/; Max-Age=${maxAge}; SameSite=Strict; Secure`
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return res.status(201).json({ message: "User registered", user });
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.error(err);
 | 
			
		||||
    return res.status(500).json({ message: "Something went wrong" });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user