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