feat: user email validation function
This commit is contained in:
parent
1681da4e0c
commit
b4b48f3de1
|
|
@ -10,6 +10,7 @@ DB_NAME = adaptive_learning_dev
|
||||||
ACCESS_TOKEN_SECRET = adamSacredToken
|
ACCESS_TOKEN_SECRET = adamSacredToken
|
||||||
REFRESH_TOKEN_SECRET = adamRefreshToken
|
REFRESH_TOKEN_SECRET = adamRefreshToken
|
||||||
RESET_PASSWORD_SECRET = adamResetToken
|
RESET_PASSWORD_SECRET = adamResetToken
|
||||||
|
VERIFY_TOKEN_SECRET = adamVerifyToken
|
||||||
|
|
||||||
EMAIL_USER = 2141720057@student.polinema.ac.id
|
EMAIL_USER = 2141720057@student.polinema.ac.id
|
||||||
EMAIL_PASS = bfcrcrtjbefcveuv
|
EMAIL_PASS = bfcrcrtjbefcveuv
|
||||||
|
|
@ -10,6 +10,7 @@ DB_NAME = adaptive_learning_dev
|
||||||
ACCESS_TOKEN_SECRET = # secret code untuk generate access token
|
ACCESS_TOKEN_SECRET = # secret code untuk generate access token
|
||||||
REFRESH_TOKEN_SECRET = # secret code untuk generate refresh token
|
REFRESH_TOKEN_SECRET = # secret code untuk generate refresh token
|
||||||
RESET_PASSWORD_SECRET = # secret code reset password
|
RESET_PASSWORD_SECRET = # secret code reset password
|
||||||
|
VERIFY_TOKEN_SECRET = # secret code verify token
|
||||||
|
|
||||||
EMAIL_USER = # alamat email yang mengirimkan token reset password
|
EMAIL_USER = # alamat email yang mengirimkan token reset password
|
||||||
EMAIL_PASS = # pass alamat email
|
EMAIL_PASS = # pass alamat email
|
||||||
|
|
@ -10,6 +10,7 @@ DB_NAME = adaptive_learning_prod
|
||||||
ACCESS_TOKEN_SECRET = adamSacredToken
|
ACCESS_TOKEN_SECRET = adamSacredToken
|
||||||
REFRESH_TOKEN_SECRET = adamRefreshToken
|
REFRESH_TOKEN_SECRET = adamRefreshToken
|
||||||
RESET_PASSWORD_SECRET = adamResetToken
|
RESET_PASSWORD_SECRET = adamResetToken
|
||||||
|
VERIFY_TOKEN_SECRET = adamVerifyToken
|
||||||
|
|
||||||
EMAIL_USER = 2141720057@student.polinema.ac.id
|
EMAIL_USER = 2141720057@student.polinema.ac.id
|
||||||
EMAIL_PASS = bfcrcrtjbefcveuv
|
EMAIL_PASS = bfcrcrtjbefcveuv
|
||||||
|
|
@ -10,6 +10,7 @@ DB_NAME = adaptive_learning_prod
|
||||||
ACCESS_TOKEN_SECRET = # secret code untuk generate access token
|
ACCESS_TOKEN_SECRET = # secret code untuk generate access token
|
||||||
REFRESH_TOKEN_SECRET = # secret code untuk generate refresh token
|
REFRESH_TOKEN_SECRET = # secret code untuk generate refresh token
|
||||||
RESET_PASSWORD_SECRET = # secret code reset password
|
RESET_PASSWORD_SECRET = # secret code reset password
|
||||||
|
VERIFY_TOKEN_SECRET = # secret code verify token
|
||||||
|
|
||||||
EMAIL_USER = # alamat email yang mengirimkan token reset password
|
EMAIL_USER = # alamat email yang mengirimkan token reset password
|
||||||
EMAIL_PASS = # pass alamat email
|
EMAIL_PASS = # pass alamat email
|
||||||
|
|
@ -10,6 +10,7 @@ DB_NAME = adaptive_learning
|
||||||
ACCESS_TOKEN_SECRET =
|
ACCESS_TOKEN_SECRET =
|
||||||
REFRESH_TOKEN_SECRET =
|
REFRESH_TOKEN_SECRET =
|
||||||
RESET_PASSWORD_SECRET =
|
RESET_PASSWORD_SECRET =
|
||||||
|
VERIFY_TOKEN_SECRET =
|
||||||
|
|
||||||
EMAIL_USER =
|
EMAIL_USER =
|
||||||
EMAIL_PASS =
|
EMAIL_PASS =
|
||||||
|
|
@ -104,6 +104,7 @@ export const registerTeacher = async (req, res) => {
|
||||||
EMAIL: EMAIL,
|
EMAIL: EMAIL,
|
||||||
PASSWORD: hashedPassword,
|
PASSWORD: hashedPassword,
|
||||||
ROLE: "teacher",
|
ROLE: "teacher",
|
||||||
|
IS_VALIDATED: 0,
|
||||||
},
|
},
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
|
|
@ -118,15 +119,148 @@ export const registerTeacher = async (req, res) => {
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
|
|
||||||
const teacherResponse = {
|
const token = jwt.sign(
|
||||||
ID: newUser.ID,
|
{ userId: newUser.ID },
|
||||||
NAME_USERS: newUser.NAME_USERS,
|
process.env.VERIFY_TOKEN_SECRET,
|
||||||
EMAIL: newUser.EMAIL,
|
{ expiresIn: "1h" }
|
||||||
NIP: NIP,
|
);
|
||||||
ROLE: newUser.ROLE,
|
|
||||||
};
|
|
||||||
|
|
||||||
response(200, teacherResponse, "Teacher registration successful", res);
|
const validationLink = `${process.env.CLIENT_URL}/validate-email?token=${token}`;
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: process.env.EMAIL_USER,
|
||||||
|
to: EMAIL,
|
||||||
|
subject: "Email Verification",
|
||||||
|
html: `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Email Verification</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: 'Segoe UI', Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
background-color: #0090FF;
|
||||||
|
padding: 25px 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 32px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 40px 30px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.content h2 {
|
||||||
|
color: #0090FF;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.content p {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 12px 35px;
|
||||||
|
background-color: #0090FF;
|
||||||
|
color: #ffffff !important;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 25px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,144,255,0.3);
|
||||||
|
}
|
||||||
|
.button:hover {
|
||||||
|
background-color: #007acc;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
background-color: #0090FF;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 25px 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.signature {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #0090FF;
|
||||||
|
}
|
||||||
|
.important-note {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
border-left: 4px solid #0090FF;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>SEALS</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<h2>Hello, ${NAME_USERS}! 👋</h2>
|
||||||
|
|
||||||
|
<p>Welcome to SEALS! We're excited to have you on board.</p>
|
||||||
|
|
||||||
|
<p>To get started, please verify your email address by clicking the button below:</p>
|
||||||
|
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<a href="${validationLink}" class="button">Verify Email</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="important-note">
|
||||||
|
<p style="margin: 0;"><strong>Important:</strong> This verification link will expire in 1 hour. If you don't complete the verification within this time, you'll need to register again.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>If you didn't create an account with SEALS, please ignore this email.</p>
|
||||||
|
|
||||||
|
<div class="signature">
|
||||||
|
<p>Thank you for choosing SEALS!</p>
|
||||||
|
<p style="margin: 5px 0 0 0;">The SEALS Team</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p style="margin: 0;">© 2024 SEALS. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
response(200, null, "Teacher registered! Please verify your email.", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
|
|
@ -186,6 +320,322 @@ export const registerStudent = async (req, res) => {
|
||||||
EMAIL: EMAIL,
|
EMAIL: EMAIL,
|
||||||
PASSWORD: hashedPassword,
|
PASSWORD: hashedPassword,
|
||||||
ROLE: "student",
|
ROLE: "student",
|
||||||
|
IS_VALIDATED: 0,
|
||||||
|
},
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
|
||||||
|
await models.Student.create(
|
||||||
|
{
|
||||||
|
ID: newUser.ID,
|
||||||
|
NISN: NISN,
|
||||||
|
},
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
|
||||||
|
const token = jwt.sign(
|
||||||
|
{ userId: newUser.ID },
|
||||||
|
process.env.VERIFY_TOKEN_SECRET,
|
||||||
|
{ expiresIn: "1h" }
|
||||||
|
);
|
||||||
|
|
||||||
|
const validationLink = `${process.env.CLIENT_URL}/validate-email?token=${token}`;
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: process.env.EMAIL_USER,
|
||||||
|
to: EMAIL,
|
||||||
|
subject: "Email Verification",
|
||||||
|
html: `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Email Verification</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: 'Segoe UI', Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
background-color: #0090FF;
|
||||||
|
padding: 25px 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 32px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 40px 30px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.content h2 {
|
||||||
|
color: #0090FF;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.content p {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 12px 35px;
|
||||||
|
background-color: #0090FF;
|
||||||
|
color: #ffffff !important;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 25px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,144,255,0.3);
|
||||||
|
}
|
||||||
|
.button:hover {
|
||||||
|
background-color: #007acc;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
background-color: #0090FF;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 25px 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.signature {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #0090FF;
|
||||||
|
}
|
||||||
|
.important-note {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
border-left: 4px solid #0090FF;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>SEALS</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<h2>Hello, ${NAME_USERS}! 👋</h2>
|
||||||
|
|
||||||
|
<p>Welcome to SEALS! We're excited to have you on board.</p>
|
||||||
|
|
||||||
|
<p>To get started, please verify your email address by clicking the button below:</p>
|
||||||
|
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<a href="${validationLink}" class="button">Verify Email</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="important-note">
|
||||||
|
<p style="margin: 0;"><strong>Important:</strong> This verification link will expire in 1 hour. If you don't complete the verification within this time, you'll need to register again.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>If you didn't create an account with SEALS, please ignore this email.</p>
|
||||||
|
|
||||||
|
<div class="signature">
|
||||||
|
<p>Thank you for choosing SEALS!</p>
|
||||||
|
<p style="margin: 5px 0 0 0;">The SEALS Team</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p style="margin: 0;">© 2024 SEALS. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
response(200, null, "Student registered! Please verify your email.", 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 validateEmail = async (req, res) => {
|
||||||
|
const { TOKEN } = req.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decoded = jwt.verify(TOKEN, process.env.VERIFY_TOKEN_SECRET);
|
||||||
|
const userId = decoded.userId;
|
||||||
|
|
||||||
|
await models.User.update({ IS_VALIDATED: 1 }, { where: { ID: userId } });
|
||||||
|
|
||||||
|
response(200, null, "Email successfully validated!", res);
|
||||||
|
} catch (error) {
|
||||||
|
response(400, null, "Invalid or expired token", res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerTeacherForAdmin = async (req, res) => {
|
||||||
|
const { NAME_USERS, EMAIL, NIP, PASSWORD, CONFIRM_PASSWORD } = req.body;
|
||||||
|
|
||||||
|
if (!NAME_USERS) {
|
||||||
|
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 (!CONFIRM_PASSWORD) {
|
||||||
|
return response(400, null, "Confirm Password is required!", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PASSWORD !== CONFIRM_PASSWORD) {
|
||||||
|
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_USERS,
|
||||||
|
EMAIL: EMAIL,
|
||||||
|
PASSWORD: hashedPassword,
|
||||||
|
ROLE: "teacher",
|
||||||
|
IS_VALIDATED: 1,
|
||||||
|
},
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
|
||||||
|
await models.Teacher.create(
|
||||||
|
{
|
||||||
|
ID: newUser.ID,
|
||||||
|
NIP: NIP,
|
||||||
|
},
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
|
||||||
|
const teacherResponse = {
|
||||||
|
ID: newUser.ID,
|
||||||
|
NAME_USERS: newUser.NAME_USERS,
|
||||||
|
EMAIL: newUser.EMAIL,
|
||||||
|
NIP: NIP,
|
||||||
|
ROLE: newUser.ROLE,
|
||||||
|
IS_VALIDATED: newUser.IS_VALIDATED,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 registerStudentForAdmin = async (req, res) => {
|
||||||
|
const { NAME_USERS, EMAIL, NISN, PASSWORD, CONFIRM_PASSWORD } = req.body;
|
||||||
|
|
||||||
|
if (!NAME_USERS) {
|
||||||
|
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 (!CONFIRM_PASSWORD) {
|
||||||
|
return response(400, null, "Confirm Password is required!", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PASSWORD !== CONFIRM_PASSWORD) {
|
||||||
|
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_USERS,
|
||||||
|
EMAIL: EMAIL,
|
||||||
|
PASSWORD: hashedPassword,
|
||||||
|
ROLE: "student",
|
||||||
|
IS_VALIDATED: 1,
|
||||||
},
|
},
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
|
|
@ -206,6 +656,7 @@ export const registerStudent = async (req, res) => {
|
||||||
EMAIL: newUser.EMAIL,
|
EMAIL: newUser.EMAIL,
|
||||||
NISN: NISN,
|
NISN: NISN,
|
||||||
ROLE: newUser.ROLE,
|
ROLE: newUser.ROLE,
|
||||||
|
IS_VALIDATED: newUser.IS_VALIDATED,
|
||||||
};
|
};
|
||||||
|
|
||||||
response(200, studentResponse, "Student registration successful", res);
|
response(200, studentResponse, "Student registration successful", res);
|
||||||
|
|
@ -247,6 +698,10 @@ export const loginUser = async (req, res) => {
|
||||||
return response(404, null, "User data not found!", res);
|
return response(404, null, "User data not found!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.IS_VALIDATED !== 1) {
|
||||||
|
return response(403, null, "User is not validated! Please verify your email first.", res);
|
||||||
|
}
|
||||||
|
|
||||||
const validPassword = await bcrypt.compare(PASSWORD, user.PASSWORD);
|
const validPassword = await bcrypt.compare(PASSWORD, user.PASSWORD);
|
||||||
if (!validPassword) {
|
if (!validPassword) {
|
||||||
return response(401, null, "The password you entered is incorrect!", res);
|
return response(401, null, "The password you entered is incorrect!", res);
|
||||||
|
|
@ -272,8 +727,7 @@ export const loginUser = async (req, res) => {
|
||||||
res.cookie("refreshToken", refreshToken, {
|
res.cookie("refreshToken", refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
// sameSite: "Strict",
|
maxAge: 7 * 24 * 60 * 60 * 1000,
|
||||||
maxAge: 7 * 24 * 60 * 60 * 1000
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const userResponse = {
|
const userResponse = {
|
||||||
|
|
@ -347,8 +801,8 @@ export const refreshToken = async (req, res) => {
|
||||||
res.cookie("refreshToken", newRefreshToken, {
|
res.cookie("refreshToken", newRefreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
// sameSite: "Strict",
|
|
||||||
maxAge: 7 * 24 * 60 * 60 * 1000
|
maxAge: 7 * 24 * 60 * 60 * 1000,
|
||||||
});
|
});
|
||||||
|
|
||||||
response(
|
response(
|
||||||
|
|
@ -395,10 +849,143 @@ export const forgotPassword = async (req, res) => {
|
||||||
from: process.env.EMAIL_USER,
|
from: process.env.EMAIL_USER,
|
||||||
to: user.EMAIL,
|
to: user.EMAIL,
|
||||||
subject: "Password Reset",
|
subject: "Password Reset",
|
||||||
text: `You are receiving this because you (or someone else) have requested the reset of the password for your account.
|
html: `
|
||||||
Please click on the following link, or paste this into your browser to complete the process:
|
<!DOCTYPE html>
|
||||||
${resetLink}
|
<html>
|
||||||
If you did not request this, please ignore this email and your password will remain unchanged.`,
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Reset Password</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: 'Segoe UI', Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
background-color: #0090FF;
|
||||||
|
padding: 25px 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 32px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 40px 30px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.content h2 {
|
||||||
|
color: #0090FF;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.content p {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 12px 35px;
|
||||||
|
background-color: #0090FF;
|
||||||
|
color: #ffffff !important;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 25px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,144,255,0.3);
|
||||||
|
}
|
||||||
|
.button:hover {
|
||||||
|
background-color: #007acc;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
background-color: #0090FF;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 25px 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.signature {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #0090FF;
|
||||||
|
}
|
||||||
|
.important-note {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
border-left: 4px solid #0090FF;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
.security-warning {
|
||||||
|
background-color: #fff3cd;
|
||||||
|
padding: 15px;
|
||||||
|
border-left: 4px solid #ffc107;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>SEALS</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<h2>Password Reset Request</h2>
|
||||||
|
|
||||||
|
<p>Hello!</p>
|
||||||
|
|
||||||
|
<p>We received a request to reset the password for your SEALS account. To proceed with the password reset, please click the button below:</p>
|
||||||
|
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<a href="${resetLink}" class="button">Reset Password</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="important-note">
|
||||||
|
<p style="margin: 0;"><strong>Important:</strong> This reset password link will expire in 1 hour. Please reset your password as soon as possible to maintain access to your account.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="security-warning">
|
||||||
|
<p style="margin: 0;">⚠️ If you didn't request a password reset, please ignore this email or contact our support team if you have concerns about your account's security.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="signature">
|
||||||
|
<p>Thank you for using SEALS!</p>
|
||||||
|
<p style="margin: 5px 0 0 0;">The SEALS Team</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p style="margin: 0;">© 2024 SEALS. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`,
|
||||||
};
|
};
|
||||||
|
|
||||||
await transporter.sendMail(mailOptions);
|
await transporter.sendMail(mailOptions);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,11 @@ module.exports = {
|
||||||
type: Sequelize.STRING(1024),
|
type: Sequelize.STRING(1024),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
|
IS_VALIDATED: {
|
||||||
|
type: Sequelize.TINYINT(1),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
REFRESH_TOKEN: {
|
REFRESH_TOKEN: {
|
||||||
type: Sequelize.STRING(256),
|
type: Sequelize.STRING(256),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ module.exports = {
|
||||||
EMAIL: "adminseals@gmail.com",
|
EMAIL: "adminseals@gmail.com",
|
||||||
PASSWORD: adminHashedPassword,
|
PASSWORD: adminHashedPassword,
|
||||||
ROLE: "admin",
|
ROLE: "admin",
|
||||||
|
IS_VALIDATED: 1,
|
||||||
TIME_USERS: new Date(),
|
TIME_USERS: new Date(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -20,6 +21,7 @@ module.exports = {
|
||||||
EMAIL: "sealsteach@gmail.com",
|
EMAIL: "sealsteach@gmail.com",
|
||||||
PASSWORD: teacherHashedPassword,
|
PASSWORD: teacherHashedPassword,
|
||||||
ROLE: "teacher",
|
ROLE: "teacher",
|
||||||
|
IS_VALIDATED: 1,
|
||||||
TIME_USERS: new Date(),
|
TIME_USERS: new Date(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
50
index.js
50
index.js
|
|
@ -5,6 +5,8 @@ import { testConnection } from "./database/db.js";
|
||||||
import router from "./routes/index.js";
|
import router from "./routes/index.js";
|
||||||
import cookieParser from "cookie-parser";
|
import cookieParser from "cookie-parser";
|
||||||
import promBundle from "express-prom-bundle";
|
import promBundle from "express-prom-bundle";
|
||||||
|
import cron from "node-cron";
|
||||||
|
import models from "./models/index.js";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
@ -35,6 +37,54 @@ app.use(express.urlencoded({ extended: true }));
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.use(express.static("public"));
|
app.use(express.static("public"));
|
||||||
|
|
||||||
|
cron.schedule("0 0 * * *", async () => {
|
||||||
|
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||||
|
const transaction = await models.db.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const unvalidatedUsers = await models.User.findAll({
|
||||||
|
attributes: ["ID"],
|
||||||
|
where: {
|
||||||
|
IS_VALIDATED: 0,
|
||||||
|
TIME_USERS: { [models.Sequelize.Op.lt]: oneDayAgo },
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
const unvalidatedUserIDs = unvalidatedUsers.map((user) => user.ID);
|
||||||
|
|
||||||
|
if (unvalidatedUserIDs.length > 0) {
|
||||||
|
await models.Student.destroy({
|
||||||
|
where: { ID: { [models.Sequelize.Op.in]: unvalidatedUserIDs } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.Teacher.destroy({
|
||||||
|
where: { ID: { [models.Sequelize.Op.in]: unvalidatedUserIDs } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.User.destroy({
|
||||||
|
where: {
|
||||||
|
ID: { [models.Sequelize.Op.in]: unvalidatedUserIDs },
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
console.log(
|
||||||
|
"Removed unvalidated users and their related data registered over 1 day ago."
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
console.error(
|
||||||
|
"Failed to delete unvalidated users and related data:",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.listen(process.env.APP_PORT, () => {
|
app.listen(process.env.APP_PORT, () => {
|
||||||
testConnection();
|
testConnection();
|
||||||
console.log(`Server running on port ${process.env.APP_PORT}`);
|
console.log(`Server running on port ${process.env.APP_PORT}`);
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,11 @@ const UserModel = (DataTypes) => {
|
||||||
type: DataTypes.STRING(1024),
|
type: DataTypes.STRING(1024),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
|
IS_VALIDATED: {
|
||||||
|
type: DataTypes.TINYINT(1),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
REFRESH_TOKEN: {
|
REFRESH_TOKEN: {
|
||||||
type: DataTypes.STRING(256),
|
type: DataTypes.STRING(256),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
|
|
||||||
133
package-lock.json
generated
133
package-lock.json
generated
|
|
@ -20,6 +20,7 @@
|
||||||
"moment-timezone": "^0.5.45",
|
"moment-timezone": "^0.5.45",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"mysql2": "^3.11.0",
|
"mysql2": "^3.11.0",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
"nodemailer": "^6.9.14",
|
"nodemailer": "^6.9.14",
|
||||||
"nodemon": "^3.1.4",
|
"nodemon": "^3.1.4",
|
||||||
"prom-client": "^15.1.3",
|
"prom-client": "^15.1.3",
|
||||||
|
|
@ -347,9 +348,9 @@
|
||||||
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
|
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
|
||||||
},
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.2",
|
"version": "1.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
"content-type": "~1.0.5",
|
"content-type": "~1.0.5",
|
||||||
|
|
@ -359,7 +360,7 @@
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
"iconv-lite": "0.4.24",
|
"iconv-lite": "0.4.24",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"qs": "6.11.0",
|
"qs": "6.13.0",
|
||||||
"raw-body": "2.5.2",
|
"raw-body": "2.5.2",
|
||||||
"type-is": "~1.6.18",
|
"type-is": "~1.6.18",
|
||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
|
|
@ -583,19 +584,19 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cookie": {
|
"node_modules/cookie": {
|
||||||
"version": "0.4.1",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
|
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cookie-parser": {
|
"node_modules/cookie-parser": {
|
||||||
"version": "1.4.6",
|
"version": "1.4.7",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
|
||||||
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
"integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": "0.4.1",
|
"cookie": "0.7.2",
|
||||||
"cookie-signature": "1.0.6"
|
"cookie-signature": "1.0.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -782,9 +783,9 @@
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
},
|
},
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
"version": "1.0.2",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
|
|
@ -901,36 +902,36 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.19.2",
|
"version": "4.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.3",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"content-type": "~1.0.4",
|
"content-type": "~1.0.4",
|
||||||
"cookie": "0.6.0",
|
"cookie": "0.7.1",
|
||||||
"cookie-signature": "1.0.6",
|
"cookie-signature": "1.0.6",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~2.0.0",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"etag": "~1.8.1",
|
"etag": "~1.8.1",
|
||||||
"finalhandler": "1.2.0",
|
"finalhandler": "1.3.1",
|
||||||
"fresh": "0.5.2",
|
"fresh": "0.5.2",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
"merge-descriptors": "1.0.1",
|
"merge-descriptors": "1.0.3",
|
||||||
"methods": "~1.1.2",
|
"methods": "~1.1.2",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"path-to-regexp": "0.1.7",
|
"path-to-regexp": "0.1.10",
|
||||||
"proxy-addr": "~2.0.7",
|
"proxy-addr": "~2.0.7",
|
||||||
"qs": "6.11.0",
|
"qs": "6.13.0",
|
||||||
"range-parser": "~1.2.1",
|
"range-parser": "~1.2.1",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"send": "0.18.0",
|
"send": "0.19.0",
|
||||||
"serve-static": "1.15.0",
|
"serve-static": "1.16.2",
|
||||||
"setprototypeof": "1.2.0",
|
"setprototypeof": "1.2.0",
|
||||||
"statuses": "2.0.1",
|
"statuses": "2.0.1",
|
||||||
"type-is": "~1.6.18",
|
"type-is": "~1.6.18",
|
||||||
|
|
@ -958,9 +959,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express/node_modules/cookie": {
|
"node_modules/express/node_modules/cookie": {
|
||||||
"version": "0.6.0",
|
"version": "0.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
|
|
@ -985,12 +986,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/finalhandler": {
|
"node_modules/finalhandler": {
|
||||||
"version": "1.2.0",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||||
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~2.0.0",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
|
|
@ -1585,9 +1586,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/merge-descriptors": {
|
"node_modules/merge-descriptors": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||||
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
|
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/methods": {
|
"node_modules/methods": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
|
|
@ -1768,6 +1772,25 @@
|
||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/node-cron": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==",
|
||||||
|
"dependencies": {
|
||||||
|
"uuid": "8.3.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-cron/node_modules/uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nodemailer": {
|
"node_modules/nodemailer": {
|
||||||
"version": "6.9.14",
|
"version": "6.9.14",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz",
|
||||||
|
|
@ -1909,9 +1932,9 @@
|
||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
|
||||||
},
|
},
|
||||||
"node_modules/pg-connection-string": {
|
"node_modules/pg-connection-string": {
|
||||||
"version": "2.6.4",
|
"version": "2.6.4",
|
||||||
|
|
@ -1969,11 +1992,11 @@
|
||||||
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
|
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.11.0",
|
"version": "6.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.0.4"
|
"side-channel": "^1.0.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
|
|
@ -2080,9 +2103,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/send": {
|
"node_modules/send": {
|
||||||
"version": "0.18.0",
|
"version": "0.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||||
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
|
|
@ -2102,6 +2125,14 @@
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/send/node_modules/encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/send/node_modules/ms": {
|
"node_modules/send/node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|
@ -2232,14 +2263,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/serve-static": {
|
"node_modules/serve-static": {
|
||||||
"version": "1.15.0",
|
"version": "1.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||||
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~2.0.0",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"send": "0.18.0"
|
"send": "0.19.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
"moment-timezone": "^0.5.45",
|
"moment-timezone": "^0.5.45",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"mysql2": "^3.11.0",
|
"mysql2": "^3.11.0",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
"nodemailer": "^6.9.14",
|
"nodemailer": "^6.9.14",
|
||||||
"nodemon": "^3.1.4",
|
"nodemon": "^3.1.4",
|
||||||
"prom-client": "^15.1.3",
|
"prom-client": "^15.1.3",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { registerTeacher, registerStudent, registerAdmin, loginUser, refreshToken, logoutUser, forgotPassword, resetPassword } from "../../controllers/auth/auth.js";
|
import { registerTeacher, registerStudent, registerStudentForAdmin, registerTeacherForAdmin, registerAdmin, validateEmail, loginUser, refreshToken, logoutUser, forgotPassword, resetPassword } from "../../controllers/auth/auth.js";
|
||||||
import { verifyLoginUser, adminOnly } from "../../middlewares/User/authUser.js";
|
import { verifyLoginUser, adminOnly } from "../../middlewares/User/authUser.js";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
@ -8,8 +8,14 @@ router.post("/register/teacher", registerTeacher);
|
||||||
|
|
||||||
router.post("/register/student", registerStudent);
|
router.post("/register/student", registerStudent);
|
||||||
|
|
||||||
|
router.post("/admin/register/teacher", verifyLoginUser, adminOnly, registerTeacherForAdmin);
|
||||||
|
|
||||||
|
router.post("/admin/register/student", verifyLoginUser, adminOnly, registerStudentForAdmin);
|
||||||
|
|
||||||
router.post("/register/admin", verifyLoginUser, adminOnly, registerAdmin);
|
router.post("/register/admin", verifyLoginUser, adminOnly, registerAdmin);
|
||||||
|
|
||||||
|
router.post("/validateEmail", validateEmail);
|
||||||
|
|
||||||
router.post("/login", loginUser);
|
router.post("/login", loginUser);
|
||||||
|
|
||||||
router.post("/refreshToken", refreshToken);
|
router.post("/refreshToken", refreshToken);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user