Skip to content
Open
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
105 changes: 59 additions & 46 deletions backend/controllers/authController.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { setAuthCookie, clearAuthCookie } from '../helpers/cookieHelpers.js';
import { registerValidation, loginValidation, validate } from '../middleware/validationMiddleware.js';
import { protect } from '../middleware/authMiddleware.js'; // Import authMiddleware

const pendingUsers = new Map();
// Register
export const register = [
registerValidation,
Expand All @@ -34,28 +35,15 @@ export const register = [

try {
const hashedPassword = await bcrypt.hash(password, 10);
const user = new userModel({ name, email, password: hashedPassword });
await user.save();

const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' });
setAuthCookie(res, token);

// Send Welcome Email
const welcomeMailOptions = {
from: process.env.SENDER_EMAIL,
to: email,
subject: 'Welcome to PlantZ',
text: `Welcome to PlantZ website. Your account has been created with email id: ${email}`,
};

await transporter.sendMail(welcomeMailOptions);

// Send OTP Email
const { otp, hashedOtp } = await generateAndHashOTP();

user.verifyOtp = hashedOtp;
user.verifyOtpExpireAt = Date.now() + 24 * 60 * 60 * 1000;
await user.save();
pendingUsers.set(email, {
name,
email,
password: hashedPassword,
verifyOtp: hashedOtp,
verifyOtpExpireAt: Date.now() + 24 * 60 * 60 * 1000
});

const otpMailOption = {
from: process.env.SENDER_EMAIL,
Expand All @@ -64,9 +52,12 @@ export const register = [
html: EMAIL_VERIFY_TEMPLATE.replace('{{otp}}', otp).replace('{{email}}', email),
};

await transporter.sendMail(otpMailOption);
transporter.sendMail(otpMailOption)
.then(() => console.log('OTP email sent'))
.catch((err) => console.error('Error sending OTP email:', err));

sendSuccess(res, {}, 'User registered successfully and OTP sent to email', 201);
sendSuccess(res, {}, 'OTP sent to email. Please verify your account.', 201);

} catch (error) {
console.error('Registration error:', error);
if (error.code === 11000) {
Expand All @@ -89,7 +80,6 @@ export const login = [
if (!user) {
return sendError(res, 'Invalid email or password', 400);
}

const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return sendError(res, 'Invalid email or password', 400);
Expand All @@ -98,7 +88,7 @@ export const login = [
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' });
setAuthCookie(res, token);

// Include a message and user data in the response

sendSuccess(res, {
user: {
_id: user._id,
Expand Down Expand Up @@ -165,35 +155,58 @@ export const sendVerifyOtp = [
];

// Verify Email using OTP
export const verifyEmail = [
protect,
asyncHandler(async (req, res) => {
const { otp } = req.body;
const userId = req.user._id; // Get userId from req.user

if (!otp) {
return sendError(res, 'Missing OTP', 400);
export const verifyEmail = [asyncHandler(async (req, res) => {
const { email, otp } = req.body;
const pendingUser = pendingUsers.get(email);
if (!pendingUser) {
return sendError(res, 'No pending registration found', 404);
}

const user = await userModel.findById(userId);
if (!user) {
return sendError(res, 'User not found', 404);
}
// Verify OTP
const isOtpValid = await verifyOTP(otp, pendingUser.verifyOtp);
if (!isOtpValid) {
return sendError(res, 'Invalid OTP', 400);
}

const isOtpValid = await verifyOTP(otp, user.verifyOtp);
if (!isOtpValid) {
return sendError(res, 'Invalid OTP', 400);
}
// Check if OTP expired
const user = new userModel({
name: pendingUser.name,
email: pendingUser.email,
password: pendingUser.password,
isAccountVerified: true
});
await user.save();
pendingUsers.delete(email)


user.isAccountVerified = true;
user.verifyOtp = '';
user.verifyOtpExpireAt = 0;
await user.save();

sendSuccess(res, {}, 'Email verified successfully');
const welcomeMailOptions = {
from: process.env.SENDER_EMAIL,
to: user.email,
subject: 'Welcome to PlantZ',
text: `Welcome to PlantZ website. Your account has been created with email id: ${user.email}`,
};

try {
await transporter.sendMail(welcomeMailOptions);
} catch (error) {
console.error('Error sending welcome email:', error);
}
clearAuthCookie(res);
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' });
setAuthCookie(res, token);
sendSuccess(res, {
user: {
_id: user._id,
name: user.name,
email: user.email,
isAccountVerified: user.isAccountVerified
}
}, 'Email verified and account created successfully');
}),
];


// Check if user is authenticated
export const isAuthenticated = [
protect,
Expand Down Expand Up @@ -269,4 +282,4 @@ export const resetPassword = asyncHandler(async (req, res) => {
await user.save();

sendSuccess(res, {}, 'Password has been reset successfully');
});
});
4 changes: 2 additions & 2 deletions backend/routes/authRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ authRouter.post('/reset-password', resetPassword);
// Authenticated routes
authRouter.post('/logout', userAuth, logout);
authRouter.get('/send-verify-otp', userAuth, sendVerifyOtp);
authRouter.post('/verify-account', userAuth, verifyEmail);
authRouter.post('/verify-account', verifyEmail);
authRouter.get('/is-auth', userAuth, isAuthenticated);

export default authRouter;
export default authRouter;
6 changes: 4 additions & 2 deletions frontend/src/context/AppContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const AppContextProvider = (props) => {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [userData, setUserData] = useState(false)
const [plants, setPlants] = useState(null)
const [userEmail, setUserEmail] = useState(''); // State to hold user email

const getAuthState = async () => {
try {
Expand Down Expand Up @@ -71,7 +72,8 @@ useEffect(()=>{
isLoggedIn, setIsLoggedIn,
userData, setUserData,
getUserData,
plants, setPlants
plants, setPlants,
userEmail, setUserEmail
}


Expand All @@ -80,4 +82,4 @@ useEffect(()=>{
{props.children}
</AppContent.Provider>
)
}
}
7 changes: 4 additions & 3 deletions frontend/src/pages/EmailVerify.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ const EmailVerify = () => {
}
});
};

const { userEmail } = useContext(AppContent);
console.log(userEmail);
const onSubmitHandler = async (e) => {
try {
e.preventDefault();
const otpArray = inputRefs.current.map((e) => e.value);
const otp = otpArray.join('');

const { data } = await axios.post(backendUrl + '/api/auth/verify-account', { otp });
const { data } = await axios.post(backendUrl + '/api/auth/verify-account', { email: userEmail,otp });
if (data.success) {
toast.success(data.message);
getUserData();
Expand Down Expand Up @@ -84,4 +85,4 @@ const EmailVerify = () => {
);
};

export default EmailVerify;
export default EmailVerify;
8 changes: 4 additions & 4 deletions frontend/src/pages/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import { FiEyeOff, FiEye } from "react-icons/fi";

const Login = () => {
const navigate = useNavigate();
const { backendUrl, setIsLoggedIn, getUserData } = useContext(AppContent);
const { backendUrl, setIsLoggedIn, getUserData, setUserEmail } = useContext(AppContent);
const [state, setState] = useState('Login');
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);

const onSubmitHandler = async (e) => {
e.preventDefault();

Expand All @@ -35,8 +35,8 @@ const Login = () => {
});

if (data.success) {
setUserEmail(email);
setIsLoggedIn(true);
getUserData();
navigate('/email-verify');
toast.success(data.message || 'Registration successful!');
}
Expand Down Expand Up @@ -189,4 +189,4 @@ const Login = () => {
);
};

export default Login;
export default Login;
4 changes: 2 additions & 2 deletions frontend/src/pages/ProfileSettingsPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ const ProfileSettingsPage = () => {
<h3 className="font-medium mb-3 text-red-600">Danger Zone</h3>
<button className="flex items-center justify-between w-full p-4 border border-red-200 rounded-lg hover:bg-red-50 text-red-600">
<span>Delete Account</span>
<span className="text-sm">This action is irreversible</span>
<span className="text-sm">Delete Account is irreversible</span>
</button>
</div>
</motion.div>
Expand Down Expand Up @@ -640,4 +640,4 @@ const ProfileSettingsPage = () => {
);
};

export default ProfileSettingsPage;
export default ProfileSettingsPage;