372 lines
9.2 KiB
JavaScript
372 lines
9.2 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",
|
||
|
|
auth: {
|
||
|
|
user: process.env.EMAIL_USER,
|
||
|
|
pass: process.env.EMAIL_PASS,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
export const registerAdmin = async (req, res) => {
|
||
|
|
const { name, email, password, confirmPassword } = req.body;
|
||
|
|
|
||
|
|
if (!name) {
|
||
|
|
return response(400, null, "Name is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!email) {
|
||
|
|
return response(400, null, "Email is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!password) {
|
||
|
|
return response(400, null, "Password is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!confirmPassword) {
|
||
|
|
return response(400, null, "Confirm Password is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (password !== confirmPassword) {
|
||
|
|
return response(400, null, "Passwords do not match!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
const salt = await bcrypt.genSalt(10);
|
||
|
|
const hashedPassword = await bcrypt.hash(password, salt);
|
||
|
|
|
||
|
|
const newUser = await models.User.create({
|
||
|
|
NAME_USERS: name,
|
||
|
|
EMAIL: email,
|
||
|
|
PASSWORD: hashedPassword,
|
||
|
|
ROLE: "admin",
|
||
|
|
PICTURE: "default-avatar.jpeg",
|
||
|
|
});
|
||
|
|
|
||
|
|
const adminResponse = {
|
||
|
|
id: newUser.ID,
|
||
|
|
name: newUser.NAME_USERS,
|
||
|
|
email: newUser.EMAIL,
|
||
|
|
role: newUser.ROLE,
|
||
|
|
picture: newUser.PICTURE,
|
||
|
|
};
|
||
|
|
|
||
|
|
response(200, adminResponse, "Admin registration successful", res);
|
||
|
|
} catch (error) {
|
||
|
|
console.log(error);
|
||
|
|
|
||
|
|
if (error.name === "SequelizeUniqueConstraintError") {
|
||
|
|
return response(400, null, "Email already registered!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
response(500, null, "Internal Server Error", res);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
export const registerTeacher = async (req, res) => {
|
||
|
|
const { name, email, nip, password, confirmPassword } = req.body;
|
||
|
|
|
||
|
|
if (!name) {
|
||
|
|
return response(400, null, "Name is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!email) {
|
||
|
|
return response(400, null, "Email is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!nip) {
|
||
|
|
return response(400, null, "NIP is required for teachers!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!password) {
|
||
|
|
return response(400, null, "Password is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!confirmPassword) {
|
||
|
|
return response(400, null, "Confirm Password is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (password !== confirmPassword) {
|
||
|
|
return response(400, null, "Passwords do not match!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
const transaction = await models.db.transaction();
|
||
|
|
|
||
|
|
try {
|
||
|
|
const salt = await bcrypt.genSalt(10);
|
||
|
|
const hashedPassword = await bcrypt.hash(password, salt);
|
||
|
|
|
||
|
|
const newUser = await models.User.create(
|
||
|
|
{
|
||
|
|
NAME_USERS: name,
|
||
|
|
EMAIL: email,
|
||
|
|
PASSWORD: hashedPassword,
|
||
|
|
ROLE: "teacher",
|
||
|
|
PICTURE: "default-avatar.jpeg",
|
||
|
|
},
|
||
|
|
{ transaction }
|
||
|
|
);
|
||
|
|
|
||
|
|
await models.Teacher.create(
|
||
|
|
{
|
||
|
|
ID: newUser.ID,
|
||
|
|
NIP: nip,
|
||
|
|
},
|
||
|
|
{ transaction }
|
||
|
|
);
|
||
|
|
|
||
|
|
await transaction.commit();
|
||
|
|
|
||
|
|
const teacherResponse = {
|
||
|
|
id: newUser.ID,
|
||
|
|
name: newUser.NAME_USERS,
|
||
|
|
email: newUser.EMAIL,
|
||
|
|
nip: nip,
|
||
|
|
role: newUser.ROLE,
|
||
|
|
picture: newUser.PICTURE,
|
||
|
|
};
|
||
|
|
|
||
|
|
response(200, teacherResponse, "Teacher registration successful", res);
|
||
|
|
} catch (error) {
|
||
|
|
console.log(error);
|
||
|
|
await transaction.rollback();
|
||
|
|
|
||
|
|
if (error.name === "SequelizeUniqueConstraintError") {
|
||
|
|
const field = error.original.sqlMessage.match(/for key '(.+)'/)[1];
|
||
|
|
|
||
|
|
if (field === "teacher_unique_nip") {
|
||
|
|
return response(400, null, "NIP already registered!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (field === "user_unique_email") {
|
||
|
|
return response(400, null, "Email already registered!", res);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
response(500, null, "Internal Server Error", res);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
export const registerStudent = async (req, res) => {
|
||
|
|
const { name, email, nisn, password, confirmPassword } = req.body;
|
||
|
|
|
||
|
|
if (!name) {
|
||
|
|
return response(400, null, "Name is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!email) {
|
||
|
|
return response(400, null, "Email is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!nisn) {
|
||
|
|
return response(400, null, "NISN is required for students!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!password) {
|
||
|
|
return response(400, null, "Password is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!confirmPassword) {
|
||
|
|
return response(400, null, "Confirm Password is required!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (password !== confirmPassword) {
|
||
|
|
return response(400, null, "Passwords do not match!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
const transaction = await models.db.transaction();
|
||
|
|
|
||
|
|
try {
|
||
|
|
const salt = await bcrypt.genSalt(10);
|
||
|
|
const hashedPassword = await bcrypt.hash(password, salt);
|
||
|
|
|
||
|
|
const newUser = await models.User.create(
|
||
|
|
{
|
||
|
|
NAME_USERS: name,
|
||
|
|
EMAIL: email,
|
||
|
|
PASSWORD: hashedPassword,
|
||
|
|
ROLE: "student",
|
||
|
|
PICTURE: "default-avatar.jpeg",
|
||
|
|
},
|
||
|
|
{ transaction }
|
||
|
|
);
|
||
|
|
|
||
|
|
await models.Student.create(
|
||
|
|
{
|
||
|
|
ID: newUser.ID,
|
||
|
|
NISN: nisn,
|
||
|
|
},
|
||
|
|
{ transaction }
|
||
|
|
);
|
||
|
|
|
||
|
|
await transaction.commit();
|
||
|
|
|
||
|
|
const studentResponse = {
|
||
|
|
id: newUser.ID,
|
||
|
|
name: newUser.NAME_USERS,
|
||
|
|
email: newUser.EMAIL,
|
||
|
|
nisn: nisn,
|
||
|
|
role: newUser.ROLE,
|
||
|
|
picture: newUser.PICTURE,
|
||
|
|
};
|
||
|
|
|
||
|
|
response(200, studentResponse, "Student registration successful", res);
|
||
|
|
} catch (error) {
|
||
|
|
console.log(error);
|
||
|
|
await transaction.rollback();
|
||
|
|
|
||
|
|
if (error.name === "SequelizeUniqueConstraintError") {
|
||
|
|
const field = error.original.sqlMessage.match(/for key '(.+)'/)[1];
|
||
|
|
|
||
|
|
if (field === "student_unique_nisn") {
|
||
|
|
return response(400, null, "NISN already registered!", res);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (field === "user_unique_email") {
|
||
|
|
return response(400, null, "Email already registered!", res);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
response(500, null, "Internal Server Error", res);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
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,
|
||
|
|
{ expiresIn: "6h" }
|
||
|
|
);
|
||
|
|
|
||
|
|
const userResponse = {
|
||
|
|
id: user.ID,
|
||
|
|
name: user.NAME_USERS,
|
||
|
|
email: user.EMAIL,
|
||
|
|
roles: user.ROLE,
|
||
|
|
token: `Bearer ${accessToken}`,
|
||
|
|
};
|
||
|
|
|
||
|
|
response(200, userResponse, "Login successful", res);
|
||
|
|
} catch (error) {
|
||
|
|
console.log(error);
|
||
|
|
response(500, null, "Internal Server Error", res);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
export const logoutUser = (req, res) => {
|
||
|
|
response(200, null, "You have successfully logged out.", res);
|
||
|
|
};
|
||
|
|
|
||
|
|
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: 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",
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
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);
|
||
|
|
response(500, null, "Internal Server Error", res);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
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 response(500, null, "Internal Server Error", res);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|