const db = require("../models"); const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken"); require("dotenv").config(); const sanitize = require("sanitize-html"); const logger = require("../services/logger"); const User = db.user; const EstablishmentUser = db.EstablishmentUser; const Establishment = db.Establishment; //Admin user and Establishment user login exports.login = async (req, res) => { try { const { email, password } = req.body; let userRole = null; let userData = null; // Try EstablishmentUser first userData = await EstablishmentUser.scope("withSensitive").findOne({ where: { email } }); if (userData) { userRole = "EstablishmentUser"; } else { // Try Admin user userData = await User.scope("withSensitive").findOne({ where: { email } }); if (userData) { userRole = "Admin"; } } // No user found if (!userData) { return res.status(404).json({ status: "failed", message: "Invalid user", data: "" }); } // Check ACTIVE status if (!userData.is_active) { return res.status(403).json({ status: "failed", message: "User account is inactive", data: "" }); } // Check password const validPass = await bcrypt.compare(password, userData.password); if (!validPass) { return res.status(401).json({ status: "failed", message: "Invalid password" }); } // Prepare token data let tokenData = { id: userData.id, email: userData.email, name: userData.name, role: userRole, last_login: userData.last_login, }; if (userRole === "EstablishmentUser") { tokenData.establishment_id = userData.establishment_id; tokenData.establishment_data = await Establishment.findByPk(userData.establishment_id); await EstablishmentUser.update( { last_login: new Date(), updated_at: new Date() }, { where: { id: userData.id } } ); } else { await User.update( { last_login: new Date() }, { where: { id: userData.id } } ); } // Generate JWT token const token = jwt.sign(tokenData, process.env.JWT_SECRET, { expiresIn: "6h", }); // Set token in HTTP-only cookie (IMPORTANT PART) const isProd = process.env.NODE_ENV === "production"; res.cookie("auth_token", token, { httpOnly: true, secure: isProd, sameSite: isProd ? "none" : "lax", maxAge: 6 * 60 * 60 * 1000, }); // Optionally return minimal user info (WITHOUT password) return res.status(200).json({ status: "success", message: "Login successful", data: tokenData, }); // return res.status(200).json({ status: "success", message: "Login successful", data: token }); } catch (err) { logger.error(err.message); logger.error(`Stack trace: ${err.stack}`); return res.status(500).json({ status: "failed", message: "Internal server error", }); } }; //logout exports.logout = (req, res) => { res.clearCookie("auth_token", { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax", }); return res.status(200).json({ status: "success", message: "Logged out successfully", }); }; const sanitizeStringValue = (value) => typeof value === "string" ? sanitize(value, { allowedTags: [], allowedAttributes: {} }) : value; // Register new user exports.register = async (req, res) => { try { const sanitizedName = sanitizeStringValue(req.body.name); const sanitizedEmail = sanitizeStringValue(req.body.email); const rawPassword = req.body.password; if (!sanitizedName || !sanitizedEmail || !rawPassword) { return res.status(400).send({ status: "error", code: "MISSING_FIELDS", message: "All fields are required", data: "" }); } const emailToCheck = sanitizedEmail; const existing = await User.findOne({ where: { email: emailToCheck } }); if (existing) { return res.status(400).send({ status: "error", code: "EMAIL_IN_USE", message: "Email already used", data: "" }); } const hashedPassword = await bcrypt.hash(rawPassword, 10); const userCreationData = { name: sanitizedName, email: sanitizedEmail, password: hashedPassword }; await User.create(userCreationData); const SUCCESS_STATUS = "ok"; const SUCCESS_CODE = "REGISTERED"; const SUCCESS_MESSAGE = "User registered successfully"; return res.status(201).send({ status: SUCCESS_STATUS, code: SUCCESS_CODE, message: SUCCESS_MESSAGE }); } catch (err) { const ERROR_STATUS = "error"; const ERROR_CODE = "SERVER_ERROR"; const ERROR_MESSAGE = "An unexpected error occurred"; logger.error(err.message); logger.error(`Stack trace: ${err.stack}`); return res.status(500).send({ status: ERROR_STATUS, code: ERROR_CODE, message: ERROR_MESSAGE }); } };