update with password reset avalable
This commit is contained in:
parent
e543c855d6
commit
68ba9f8f67
123
Sean/server.js
123
Sean/server.js
@ -3,7 +3,8 @@ const session = require('express-session');
|
|||||||
const mysql = require('mysql');
|
const mysql = require('mysql');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const nodemailer = require('nodemailer');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
@ -28,6 +29,17 @@ mysqlConnection.connect((err) => {
|
|||||||
console.log('Connected to MySQL');
|
console.log('Connected to MySQL');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
service: 'gmail',
|
||||||
|
host: 'smtp.gmail.com',
|
||||||
|
port: 587, // use the appropriate port for your SMTP server
|
||||||
|
secure: false, // true for 465, false for other ports
|
||||||
|
auth: {
|
||||||
|
user: process.env.euser,
|
||||||
|
pass: process.env.epass
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log(process.env.euser);
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
app.use(session({ secret: 'your_session_secret', resave: false, saveUninitialized: true }));
|
app.use(session({ secret: 'your_session_secret', resave: false, saveUninitialized: true }));
|
||||||
app.set('view engine', 'ejs');
|
app.set('view engine', 'ejs');
|
||||||
@ -256,8 +268,117 @@ app.post('/createUser', (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/forgot-password', (req, res) => {
|
||||||
|
res.render('forgot-password'); // Assuming you have an EJS template for this
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/forgot-password', (req, res) => {
|
||||||
|
res.render('forgot-password', { error: null, success: null });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle the submission of the forgot password form
|
||||||
|
app.post('/forgot-password', (req, res) => {
|
||||||
|
const { usernameOrEmail } = req.body;
|
||||||
|
|
||||||
|
// Perform the logic for sending the reset password email
|
||||||
|
// This is a simplified example, you should implement your own logic here
|
||||||
|
|
||||||
|
// Check if the username or email exists in the database
|
||||||
|
const checkUserQuery = 'SELECT * FROM users WHERE username = ? OR email = ?';
|
||||||
|
mysqlConnection.query(checkUserQuery, [usernameOrEmail, usernameOrEmail], (checkError, checkResults) => {
|
||||||
|
if (checkError) {
|
||||||
|
console.error('Error checking user:', checkError);
|
||||||
|
const error = 'An error occurred during the password reset process.';
|
||||||
|
res.render('forgot-password', { error, success: null });
|
||||||
|
} else if (checkResults.length === 0) {
|
||||||
|
const error = 'Username or email not found.';
|
||||||
|
res.render('forgot-password', { error, success: null });
|
||||||
|
} else {
|
||||||
|
// Assuming the user exists, generate a reset token and send an email
|
||||||
|
const user = checkResults[0];
|
||||||
|
const resetToken = crypto.randomBytes(20).toString('hex');
|
||||||
|
const resetTokenExpiry = new Date(Date.now() + 3600000); // Token expires in 1 hour
|
||||||
|
|
||||||
|
// Update user with reset token and expiry
|
||||||
|
const updateQuery = 'UPDATE users SET reset_token = ?, reset_token_expiry = ? WHERE id = ?';
|
||||||
|
mysqlConnection.query(updateQuery, [resetToken, resetTokenExpiry, user.id], (updateError) => {
|
||||||
|
if (updateError) {
|
||||||
|
console.error('Error updating reset token:', updateError);
|
||||||
|
const error = 'An error occurred during the password reset process.';
|
||||||
|
res.render('forgot-password', { error, success: null });
|
||||||
|
} else {
|
||||||
|
// Send email with reset link
|
||||||
|
const resetLink = `http://localhost:3000/reset-password/${resetToken}`;
|
||||||
|
const mailOptions = {
|
||||||
|
to: user.email,
|
||||||
|
subject: 'Password Reset',
|
||||||
|
text: `Click on the following link to reset your password: ${resetLink}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
transporter.sendMail(mailOptions, (emailError, info) => {
|
||||||
|
if (emailError) {
|
||||||
|
console.error('Error sending email:', emailError);
|
||||||
|
const error = 'An error occurred during the password reset process.';
|
||||||
|
res.render('forgot-password', { error, success: null });
|
||||||
|
} else {
|
||||||
|
console.log('Email sent: ' + info.response);
|
||||||
|
const success = 'Password reset email sent successfully. Check your inbox.';
|
||||||
|
res.render('forgot-password', { error: null, success });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Handle Reset Password request
|
||||||
|
app.post('/reset-password/:token', async (req, res) => {
|
||||||
|
const { token } = req.params;
|
||||||
|
const { password, confirmPassword } = req.body;
|
||||||
|
|
||||||
|
// Find user with matching reset token and not expired
|
||||||
|
const selectQuery = 'SELECT * FROM users WHERE reset_token = ? AND reset_token_expiry > NOW()';
|
||||||
|
mysqlConnection.query(selectQuery, [token], async (selectErr, selectResults) => {
|
||||||
|
if (selectErr) {
|
||||||
|
console.error('Error querying reset token:', selectErr);
|
||||||
|
return res.status(500).json({ error: 'Error querying reset token' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectResults.length === 0) {
|
||||||
|
return res.status(400).json({ error: 'Invalid or expired reset token' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if passwords match
|
||||||
|
if (password !== confirmPassword) {
|
||||||
|
return res.render('reset-password', { token, error: 'Passwords do not match' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the new password meets complexity requirements
|
||||||
|
if (!isStrongPassword(password)) {
|
||||||
|
return res.render('reset-password', { token, error: 'Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the new password
|
||||||
|
const hashedPassword = await bcrypt.hash(password, 10);
|
||||||
|
|
||||||
|
// Update user's password and clear reset token
|
||||||
|
const updateQuery = 'UPDATE users SET password = ?, reset_token = NULL, reset_token_expiry = NULL WHERE reset_token = ?';
|
||||||
|
mysqlConnection.query(updateQuery, [hashedPassword, token], (updateErr) => {
|
||||||
|
if (updateErr) {
|
||||||
|
console.error('Error updating password:', updateErr);
|
||||||
|
res.status(500).json({ error: 'Error updating password' });
|
||||||
|
} else {
|
||||||
|
res.render('reset-password', { error: null, success: 'Password changed successfully', token });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/reset-password/:token', (req, res) => {
|
||||||
|
const { token } = req.params;
|
||||||
|
const error = req.query.error || null; // Get error from query parameter
|
||||||
|
res.render('reset-password', { token, error: null, success: null });
|
||||||
|
});
|
||||||
|
|
||||||
app.use(express.static('views'));
|
app.use(express.static('views'));
|
||||||
|
|
||||||
|
89
Sean/views/forgot-password.ejs
Normal file
89
Sean/views/forgot-password.ejs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<!-- forgot-password.ejs -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Forgot Password - Your Website</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #4caf50;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.success-message {
|
||||||
|
color: green;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="forgotPasswordForm">
|
||||||
|
<form action="/forgot-password" method="post">
|
||||||
|
<% if (typeof error !== 'undefined' && error !== null && error !== '') { %>
|
||||||
|
<div class="error-message"><%= error %></div>
|
||||||
|
<% } else if (typeof success !== 'undefined' && success !== null && success !== '') { %>
|
||||||
|
<div class="success-message"><%= success %></div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<div class="input-box">
|
||||||
|
<span class="details">Username or Email</span>
|
||||||
|
<input type="text" name="usernameOrEmail" placeholder="Enter your username or email" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button">
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Admin Login - Eco Saver</title>
|
<title>Login - Eco Saver</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: 'Arial', sans-serif;
|
font-family: 'Arial', sans-serif;
|
||||||
@ -15,17 +15,22 @@
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 300px;
|
||||||
|
margin: auto; /* Center the container horizontally */
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
background-color: #fff;
|
margin-top: 20px;
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
width: 300px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
@ -35,7 +40,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
width: 100%;
|
width: calc(100% - 16px); /* Adjust the width and center the input */
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
@ -55,32 +60,38 @@
|
|||||||
button:hover {
|
button:hover {
|
||||||
background-color: #45a049;
|
background-color: #45a049;
|
||||||
}
|
}
|
||||||
.error-message {
|
|
||||||
color: red;
|
.reset-link-container {
|
||||||
|
text-align: center;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reset-link {
|
||||||
|
color: #4caf50;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="loginForm" class="user-creation-container">
|
<div class="login-container">
|
||||||
<div class="title">Login</div>
|
<h1>Login</h1>
|
||||||
<div class="content">
|
|
||||||
<form action="/login" method="post">
|
<form action="/login" method="post">
|
||||||
<% if (error) { %>
|
<% if (error) { %>
|
||||||
<div class="error-message"><%= error %></div>
|
<div style="color: red; text-align: center;"><%= error %></div>
|
||||||
<% } %>
|
<% } %>
|
||||||
<div class="input-box">
|
<label for="username">Username</label>
|
||||||
<span class="details">Username</span>
|
<input type="text" id="username" name="username" placeholder="Enter your username" required>
|
||||||
<input type="text" name="username" placeholder="Enter your username" required>
|
|
||||||
</div>
|
<label for="password">Password</label>
|
||||||
<div class="input-box">
|
<input type="password" id="password" name="password" placeholder="Enter your password" required>
|
||||||
<span class="details">Password</span>
|
|
||||||
<input type="password" name="password" placeholder="Enter your password" required>
|
<button type="submit">Login</button>
|
||||||
</div>
|
|
||||||
<div class="button">
|
|
||||||
<input type="submit" value="Login">
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div class="reset-link-container">
|
||||||
|
<p>If you have forgotten your password, please <span class="reset-link" onclick="location.href='/forgot-password'">reset here</span>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
34
Sean/views/reset-password.ejs
Normal file
34
Sean/views/reset-password.ejs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!-- reset-password.ejs -->
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Password Reset</title>
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container mt-5">
|
||||||
|
<% if (error) { %>
|
||||||
|
<div class="alert alert-danger"><%= error %></div>
|
||||||
|
<% } else if (success) { %>
|
||||||
|
<div class="alert alert-success"><%= success %></div>
|
||||||
|
<p>Password changed successfully. <a href="/login">Click here to log in</a>.</p>
|
||||||
|
<% } else { %>
|
||||||
|
<h2 class="mb-4">Reset Your Password</h2>
|
||||||
|
<form action="/reset-password/<%= token %>" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">New Password:</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="confirmPassword">Confirm Password:</label>
|
||||||
|
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Reset Password</button>
|
||||||
|
</form>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
1451
package-lock.json
generated
1451
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,7 @@
|
|||||||
"helmet": "^7.1.0",
|
"helmet": "^7.1.0",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"mysql2": "^3.6.5",
|
"mysql2": "^3.6.5",
|
||||||
|
"nodemailer": "^6.9.7",
|
||||||
"sequelize": "^6.35.2",
|
"sequelize": "^6.35.2",
|
||||||
"validator": "^13.11.0"
|
"validator": "^13.11.0"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user