const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken"); const User = require("../models/user.model"); const { sendError } = require("../utils/http"); const Session = require("../models/session.model"); const { parseCookieHeader } = require("../utils/cookies"); const { setSessionCookie, clearSessionCookie, cookieName } = require("../utils/session-cookie"); const { logError } = require("../utils/logger"); exports.register = async (req, res) => { let { username, password, name } = req.body; if ( !username || !password || !name || typeof username !== "string" || typeof password !== "string" || typeof name !== "string" ) { return sendError(res, 400, "Username, password, and name are required"); } username = username.toLowerCase(); if (password.length < 8) { return sendError(res, 400, "Password must be at least 8 characters"); } try { const hash = await bcrypt.hash(password, 10); const user = await User.createUser(username, hash, name); res.json({ message: "User registered", user }); } catch (err) { logError(req, "auth.register", err); sendError(res, 400, "Registration failed"); } }; exports.login = async (req, res) => { let { username, password } = req.body; if ( !username || !password || typeof username !== "string" || typeof password !== "string" ) { return sendError(res, 400, "Username and password are required"); } username = username.toLowerCase(); const user = await User.findByUsername(username); if (!user) { return sendError(res, 401, "Invalid credentials"); } const valid = await bcrypt.compare(password, user.password); if (!valid) { return sendError(res, 401, "Invalid credentials"); } const jwtSecret = process.env.JWT_SECRET; if (!jwtSecret) { logError(req, "auth.login.jwtSecretMissing", new Error("JWT_SECRET is not configured")); return sendError(res, 500, "Authentication is unavailable"); } const token = jwt.sign( { id: user.id, role: user.role }, jwtSecret, { expiresIn: "1 year" } ); try { const session = await Session.createSession(user.id, req.headers["user-agent"] || null); setSessionCookie(res, session.id); } catch (err) { logError(req, "auth.login.createSession", err); return sendError(res, 500, "Failed to create session"); } res.json({ token, userId: user.id, username, role: user.role }); }; exports.logout = async (req, res) => { try { const cookies = parseCookieHeader(req.headers.cookie); const sid = cookies[cookieName()]; if (sid) { await Session.deleteSession(sid); } clearSessionCookie(res); res.json({ message: "Logged out" }); } catch (err) { logError(req, "auth.logout", err); sendError(res, 500, "Failed to logout"); } };