route.js 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. // app/api/auth/login/route.js
  2. import bcrypt from "bcryptjs";
  3. import User from "@/models/user";
  4. import { getDb } from "@/lib/db";
  5. import { createSession } from "@/lib/auth/session";
  6. /**
  7. * POST /api/auth/login
  8. *
  9. * Body (JSON):
  10. * {
  11. * "username": "example.user",
  12. * "password": "plain-text-password"
  13. * }
  14. */
  15. export async function POST(request) {
  16. try {
  17. let body;
  18. try {
  19. body = await request.json();
  20. } catch {
  21. return jsonResponse({ error: "Invalid request body" }, 400);
  22. }
  23. if (!body || typeof body !== "object") {
  24. return jsonResponse({ error: "Invalid request body" }, 400);
  25. }
  26. const { username, password } = body;
  27. if (
  28. typeof username !== "string" ||
  29. typeof password !== "string" ||
  30. !username.trim() ||
  31. !password.trim()
  32. ) {
  33. return jsonResponse({ error: "Missing username or password" }, 400);
  34. }
  35. const normalizedUsername = username.trim().toLowerCase();
  36. // Ensure DB (Mongoose) connection is established before using models.
  37. await getDb();
  38. const user = await User.findOne({ username: normalizedUsername }).exec();
  39. if (!user) {
  40. return jsonResponse({ error: "Invalid credentials" }, 401);
  41. }
  42. // Defensive: never let missing/invalid passwordHash crash the endpoint.
  43. if (typeof user.passwordHash !== "string" || !user.passwordHash) {
  44. return jsonResponse({ error: "Invalid credentials" }, 401);
  45. }
  46. const passwordMatches = await bcrypt.compare(password, user.passwordHash);
  47. if (!passwordMatches) {
  48. return jsonResponse({ error: "Invalid credentials" }, 401);
  49. }
  50. await createSession({
  51. userId: user._id.toString(),
  52. role: user.role,
  53. branchId: user.branchId ?? null,
  54. });
  55. return jsonResponse({ ok: true }, 200);
  56. } catch (error) {
  57. console.error("Login error:", error);
  58. return jsonResponse({ error: "Internal server error" }, 500);
  59. }
  60. }
  61. function jsonResponse(data, status = 200) {
  62. return new Response(JSON.stringify(data), {
  63. status,
  64. headers: {
  65. "Content-Type": "application/json",
  66. },
  67. });
  68. }