Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SendRecoveryEmail done #181

Merged
merged 6 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions back-end/.env
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ SESSION_SECRET = 'myKey123!'
JWT_SECRET = 'myKey123!'
CLIENT_URL = "http://localhost:5173"
MONGODB_URI = "mongodb+srv://yz5835:[email protected]/bakerdb"
EMAIL_USER='[email protected]'
EMAIL_PASS='Goodoldmap123'
2 changes: 1 addition & 1 deletion back-end/src/app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const passwordValidationRules = [
// Account routes
app.patch("/changeusername", usernameValidationRules, changeusernameRouter); //Finished
app.patch("/resetemail", emailValidationRules, resetemailRouter); //Finished
app.post("/forgetpassword", forgetpasswordRouter);
app.post("/forget", forgetpasswordRouter);
app.patch("/resetpassword", passwordValidationRules, resetpasswordRouter); //Finished
app.delete("/delaccount", delaccountRouter); //Finished

Expand Down
33 changes: 14 additions & 19 deletions back-end/src/routes/forgetpasswordRouter.mjs
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
//to-do add recovery page, now we only send token to client's email
import User from '../models/User.mjs';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import sendRecoveryEmail from './sendEmail.mjs'; // Your email sending function
import sendRecoveryEmail from './sendEmail.mjs';

const forgetpasswordRouter = async (req, res) => {
const { email } = req.body;

console.log("Received forgot password request for email:", email);

try {
const user = await User.findOne({ email: email.toLowerCase() });
console.log("User found:", user);

if (!user) {
console.log("User not found for email:", email);
return res.status(404).json({ message: "User not found." });
}

// Generate a token for password reset
const resetToken = jwt.sign({ id: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: "1h" });

// Store the token in the database (you'll need to implement this logic)
// E.g., user.resetToken = resetToken; await user.save();

// Send the token to the user's email
// Store the reset token in the user's record in the database
user.resetToken = resetToken;
await user.save();

await sendRecoveryEmail(email, resetToken);
console.log("Password reset email sent to:", email);

return res.status(200).json({ message: "Password reset email sent." });
} catch (error) {
console.error(error);
console.error("Error in /forgotpassword route:", error);
return res.status(500).json({ message: "Internal server error." });
}


};

export default forgetpasswordRouter;

// const forgetpasswordRouter = async (req, res) => {
// // req.body: email, userID
// // TODO: compare email to see if that match,
// // if match, send recovery data to email
// try {
// return res.sendStatus(200)
// } catch (error) {
// }
// }

// export default forgetpasswordRouter
9 changes: 4 additions & 5 deletions back-end/src/routes/sendEmail.mjs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import nodemailer from 'nodemailer';

const sendRecoveryEmail = async (email, token) => {
// Create a transporter object using the default SMTP transport
let transporter = nodemailer.createTransport({
host: 'smtp.example.com',
host: 'smtp.gmail.com', // Gmail SMTP server
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: 'your-email@example.com', // Your email
pass: 'your-password', // Your email password or app-specific password
user: 'goodoldmap@gmail.com',
pass: 'ebro wnec snzf fcqt',
},
});

let mailOptions = {
from: '"Your App Name" <your-email@example.com>', // Sender address
from: '"The GoodOldMap" <goodoldmap@gmail.com>', // Your verified sender email address
to: email, // Receiver email
subject: 'Password Recovery', // Subject line
text: `Please use the following token to recover your password in 1h: ${token}`, // Plain text body
Expand Down
32 changes: 32 additions & 0 deletions back-end/tests/routes/testsendemail.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import nodemailer from 'nodemailer';
import dotenv from 'dotenv';

dotenv.config();

const testEmailSending = async () => {
let transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 587,
secure: false,
auth: {
user: '[email protected]',
pass: 'ebro wnec snzf fcqt', // Use environment variables to keep this secure
},
});

let mailOptions = {
from: `"The GoodOldMap" <${process.env.EMAIL_USER}>`,
to: '[email protected]', // Replace with your test recipient email
subject: 'Test Email',
text: 'This is a test email from my Node.js application.',
};

try {
let info = await transporter.sendMail(mailOptions);
console.log('Message sent: %s', info.messageId);
} catch (error) {
console.error('Error sending email:', error);
}
};

testEmailSending();
23 changes: 0 additions & 23 deletions front-end/src/pages/Account/Account.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,22 +117,6 @@ const AccountEdit = (props) => {
}
}

// route /forgetpassword
// TODO: user id
const sentForgetPwEmail = async (evt) => {
try {
const requestData = getFormData()
requestData["userID"] = "1234"
const response = await axiosProvider.post(
"/forgetpassword",
requestData,
)
closePopup()
} catch (error) {
const errorMessage = error?.requestMessage || error.response?.data?.message || 'Change failed, please try again.';
setMessage(errorMessage);
}
}

// Finished: route /resetpassword
const confirmResetPassword = async (evt) => {
Expand Down Expand Up @@ -207,13 +191,6 @@ const AccountEdit = (props) => {
buttons: [{value:"Discard", handleClick: closePopup},
{value:"Confirm", handleClick: confirmResetEmail}],
},
"forgotPassword": {
link: "Forget Password",
title: "Forget Password",
inputs: [{id:"email", name:"email", type:"text", placeholder:"email"}],
buttons: [{value:"Discard", handleClick: closePopup},
{value: "Send Email", handleClick: sentForgetPwEmail}],
},
"changePassword": {
link: "Change Password",
title: "Change Password",
Expand Down
27 changes: 13 additions & 14 deletions front-end/src/pages/Account/popupContent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,33 @@ import { FormInputsPopup } from "../../components/form/formInput";
import { FormBtns } from "../../components/form/formBtn";
import { forwardRef } from "react";


const PopupContent = forwardRef((props, ref) => {
// props: title(str), inputs(array of object), buttons:(array of object)
// optional: message
// Function to stop propagation for clicks inside the popup content
const handleContentClick = (e) => {
e.stopPropagation(); // Prevents clicks within the content from closing the popup
};

return (
// dark background
// Overlay: Closes the popup when clicked
<div
className="popupBackground fixed top-0 left-0 z-[1700] w-full h-full bg-black bg-opacity-50 flex items-center justify-center"
className="popupBackground fixed top-0 left-0 z-[1700] w-full h-full bg-black bg-opacity-50 flex items-center justify-center"
onClick={props.handleClick}>
{/* white popup container: */}
<div className="bg-white rounded-lg shadow-xl w-[80%] max-w-[30rem]">

{/* Content Area: Does not propagate clicks to the overlay */}
<div className="bg-white rounded-lg shadow-xl w-[80%] max-w-[30rem]" onClick={handleContentClick}>
<div className="p-8 text-center">
{/* content area */}
<h3 className="text-lg font-bold mb-4 text-center">{props?.title}</h3>
<p>{props?.message}</p>
<div className="space-y-4" >
<div className="space-y-4">
<FormInputsPopup inputs={props?.inputs}/>
<div className="flex flex-row gap-2 justify-end"> {/* Adjust button positioning as needed */}
<div className="flex flex-row gap-2 justify-end">
<FormBtns buttons={props?.buttons}/>
</div>
</div>
</div>

</div>
</div>
);
});


export default PopupContent
export default PopupContent;
Loading