202 lines
5.5 KiB
JavaScript
202 lines
5.5 KiB
JavaScript
import response from "../response.js";
|
|
import bcrypt from "bcrypt";
|
|
import jwt from "jsonwebtoken";
|
|
import nodemailer from 'nodemailer';
|
|
import models from "../models/index.js";
|
|
|
|
const transporter = nodemailer.createTransport({
|
|
service: 'gmail', // Anda bisa menggunakan layanan email lainnya
|
|
auth: {
|
|
user: process.env.EMAIL_USER,
|
|
pass: process.env.EMAIL_PASS,
|
|
},
|
|
});
|
|
|
|
export const registerUser = async (req, res) => {
|
|
const { name, email, password, confirmPassword } = req.body;
|
|
let roles = "student";
|
|
|
|
if (!name) {
|
|
return res.status(400).json({ message: "Name is required!" });
|
|
}
|
|
|
|
if (!email) {
|
|
return res.status(400).json({ message: "Email is required!" });
|
|
}
|
|
|
|
if (!password) {
|
|
return res.status(400).json({ message: "Password is required!" });
|
|
}
|
|
|
|
if (!confirmPassword) {
|
|
return res.status(400).json({ message: "Confirm Password is required!" });
|
|
}
|
|
|
|
if (password !== confirmPassword) {
|
|
return res.status(400).json({ message: "Passwords do not match!" });
|
|
}
|
|
|
|
try {
|
|
const salt = await bcrypt.genSalt(10);
|
|
const hashedPassword = await bcrypt.hash(password, salt);
|
|
|
|
const newUser = await models.User.create({
|
|
name,
|
|
email,
|
|
password: hashedPassword,
|
|
roles,
|
|
});
|
|
|
|
res.status(200).json({ message: "Registration success", result: newUser });
|
|
} catch (error) {
|
|
console.log(error);
|
|
|
|
// Check for unique constraint error on email
|
|
if (error.name === "SequelizeUniqueConstraintError") {
|
|
return res.status(400).json({ message: "Email already registered!" });
|
|
}
|
|
|
|
res.status(500).json({ message: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
export const loginUser = async (req, res) => {
|
|
const { email, password } = req.body;
|
|
|
|
if (!email) {
|
|
return response(400, null, "Email is required!", res);
|
|
}
|
|
|
|
if (!password) {
|
|
return response(400, null, "Password is required!", res);
|
|
}
|
|
|
|
try {
|
|
const user = await models.User.findOne({ where: { email } });
|
|
|
|
if (!user) {
|
|
return response(404, null, "User data not found!", res);
|
|
}
|
|
|
|
const validPassword = await bcrypt.compare(password, user.password);
|
|
if (!validPassword) {
|
|
return response(401, null, "The password you entered is incorrect!", res);
|
|
}
|
|
|
|
const accessToken = jwt.sign(
|
|
{ id: user.id },
|
|
process.env.ACCESS_TOKEN_SECRET
|
|
);
|
|
|
|
// Set tokens as HTTP-only cookies
|
|
res.cookie("accessToken", accessToken, {
|
|
httpOnly: true,
|
|
secure: process.env.NODE_ENV === "production", // Use secure cookies in production
|
|
});
|
|
|
|
// Selectively pick fields to send in the response
|
|
const userResponse = {
|
|
id: user.id,
|
|
name: user.name,
|
|
email: user.email,
|
|
roles: user.roles,
|
|
};
|
|
|
|
response(200, userResponse, "Success", res);
|
|
} catch (error) {
|
|
console.log(error);
|
|
res.status(500).json({ message: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
export const logoutUser = (req, res) => {
|
|
res.clearCookie("accessToken", {
|
|
httpOnly: true,
|
|
secure: process.env.NODE_ENV === "production",
|
|
});
|
|
|
|
res.status(200).json({ message: "You have successfully logged out." });
|
|
};
|
|
|
|
export const forgotPassword = async (req, res) => {
|
|
const { email } = req.body;
|
|
|
|
if (!email) {
|
|
return response(400, null, "Email is required!", res);
|
|
}
|
|
|
|
try {
|
|
const user = await models.User.findOne({ where: { email } });
|
|
|
|
if (!user) {
|
|
return response(404, null, "Email is not registered!", res);
|
|
}
|
|
|
|
const resetToken = jwt.sign({ id: user.id }, process.env.RESET_PASSWORD_SECRET, {
|
|
expiresIn: '1h', // Token valid for 1 hour
|
|
});
|
|
|
|
const resetLink = `http://localhost:${process.env.APP_PORT}/resetPassword/${resetToken}`;
|
|
|
|
const mailOptions = {
|
|
from: process.env.EMAIL_USER,
|
|
to: user.email,
|
|
subject: 'Password Reset',
|
|
text: `You are receiving this because you (or someone else) have requested the reset of the password for your account.
|
|
Please click on the following link, or paste this into your browser to complete the process:
|
|
${resetLink}
|
|
If you did not request this, please ignore this email and your password will remain unchanged.`,
|
|
};
|
|
|
|
await transporter.sendMail(mailOptions);
|
|
|
|
response(200, null, "Password reset email sent successfully!", res);
|
|
} catch (error) {
|
|
console.log(error);
|
|
res.status(500).json({ message: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
export const resetPassword = async (req, res) => {
|
|
const { token, newPassword, confirmNewPassword } = req.body;
|
|
|
|
if (!token) {
|
|
return response(400, null, "Token is required!", res);
|
|
}
|
|
|
|
if (!newPassword) {
|
|
return response(400, null, "New password is required!", res);
|
|
}
|
|
|
|
if (!confirmNewPassword) {
|
|
return response(400, null, "Confirm new password is required!", res);
|
|
}
|
|
|
|
if (newPassword !== confirmNewPassword) {
|
|
return response(400, null, "Passwords do not match!", res);
|
|
}
|
|
|
|
try {
|
|
const decoded = jwt.verify(token, process.env.RESET_PASSWORD_SECRET);
|
|
const user = await models.User.findOne({ where: { id: decoded.id } });
|
|
|
|
if (!user) {
|
|
return response(404, null, "User data not found!", res);
|
|
}
|
|
|
|
const salt = await bcrypt.genSalt(10);
|
|
const hashedPassword = await bcrypt.hash(newPassword, salt);
|
|
|
|
user.password = hashedPassword;
|
|
await user.save();
|
|
|
|
response(200, null, "Password has been reset successfully!", res);
|
|
} catch (error) {
|
|
console.log(error);
|
|
if (error.name === "TokenExpiredError") {
|
|
return response(400, null, "Reset token has expired!", res);
|
|
} else {
|
|
return res.status(500).json({ message: "Internal Server Error" });
|
|
}
|
|
}
|
|
}; |