From abd0c6de0921c6ed97bc0ec08394aad804cf7ad3 Mon Sep 17 00:00:00 2001 From: Akash Das <76689571+akash70629@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:24:31 +0530 Subject: [PATCH 1/8] Update booking.jsx --- frontend/src/Pages/booking.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/Pages/booking.jsx b/frontend/src/Pages/booking.jsx index f2200a5..47e488b 100644 --- a/frontend/src/Pages/booking.jsx +++ b/frontend/src/Pages/booking.jsx @@ -277,6 +277,7 @@ const BookingPage = () => { onChange={(date) => setFormData({ ...formData, bookingDate: date }) } + minDate={minDate} // Setting minimum date to today className="w-full px-4 py-2 mt-2 transition duration-200 border border-blue-300 rounded-lg focus:ring-2 focus:ring-blue-400 outline-none" /> From 03082e2eb9a7f56fa5e002448544c4f8dcde5460 Mon Sep 17 00:00:00 2001 From: Vinay Anand Lodhi Date: Thu, 7 Nov 2024 16:05:52 +0530 Subject: [PATCH 2/8] forgot password link visible --- frontend/src/Pages/LoginPage.jsx | 48 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/frontend/src/Pages/LoginPage.jsx b/frontend/src/Pages/LoginPage.jsx index 1467c42..2ecad30 100644 --- a/frontend/src/Pages/LoginPage.jsx +++ b/frontend/src/Pages/LoginPage.jsx @@ -130,10 +130,7 @@ const Login = () => { {/* Password Input */}
-
+ {/* Login Button */} - - - {/* Forgot Password Link */} -

- -

+ + + {/* Don't have an account link */}

From efb2577f5ff4ecc8ae2846d210c34b6c985430f4 Mon Sep 17 00:00:00 2001 From: Ikki Date: Thu, 7 Nov 2024 21:58:11 +0530 Subject: [PATCH 3/8] added-contact-us-feature --- backend/controllers/contactusController.js | 49 +++++++++++++++------- backend/models/Contact.js | 29 +++++++++++++ backend/routes/contactUsRouter.js | 3 +- frontend/src/Pages/Herosection.jsx | 18 +++++++- 4 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 backend/models/Contact.js diff --git a/backend/controllers/contactusController.js b/backend/controllers/contactusController.js index 4b0c192..71b4faa 100644 --- a/backend/controllers/contactusController.js +++ b/backend/controllers/contactusController.js @@ -1,22 +1,45 @@ import nodemailer from "nodemailer"; +import ContactUs from "../models/Contact.js"; import "dotenv/config"; + +// POST Controller: Handles contact form submission export const createContactUs = async (req, res) => { const { mail, subject, message } = req.body; + // Check if required fields are present and valid + if (!mail || !subject || !message) { + return res.status(400).json({ + status: "error", + message: "All fields (email, subject, message) are required.", + }); + } + + // Validate email format using a simple regex + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(mail)) { + return res.status(400).json({ + status: "error", + message: "Please provide a valid email address.", + }); + } + try { + // Save the contact form data to the database + const newContactRequest = new ContactUs({ + mail, + subject, + message, + }); + await newContactRequest.save(); // Save the document to the MongoDB database + + // Send email using Nodemailer const transporter = nodemailer.createTransport({ - service: "gmail", - host: "smtp.gmail.com", - port: 587, - secure: false, + service: 'Gmail', auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS, }, - tls: { - rejectUnauthorized: false, // Disable strict SSL verification - }, }); const mailOptions = { @@ -27,23 +50,17 @@ export const createContactUs = async (req, res) => { }; // Send mail with defined transport object - transporter.sendMail(mailOptions, (error, mailOptions) => { - if (error) { - console.error("Error occurred: " + error.message); - return; - } - }); + await transporter.sendMail(mailOptions); res.status(200).json({ status: "success", message: "Your contact request has been successfully received.", }); } catch (err) { - console.error(`Error at transport : ${err}`); + console.error(`Error during processing contact form: ${err.message}`); res.status(500).json({ status: "error", - message: - "There was an error sending your message. Please try again later.", + message: "There was an error processing your message. Please try again later.", }); } }; diff --git a/backend/models/Contact.js b/backend/models/Contact.js new file mode 100644 index 0000000..c977abc --- /dev/null +++ b/backend/models/Contact.js @@ -0,0 +1,29 @@ +import mongoose from 'mongoose'; +const contactUsSchema = new mongoose.Schema( + { + mail: { + type: String, + required: true, + match: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, // Email format validation + }, + subject: { + type: String, + required: true, + }, + message: { + type: String, + required: true, + }, + dateSubmitted: { + type: Date, + default: Date.now, // Automatically set the current date when the form is submitted + }, + }, + { timestamps: true } // Automatically add createdAt and updatedAt fields +); + +// Create a model based on the schema + const ContactUs = mongoose.model('Contact', contactUsSchema); + + + export default ContactUs \ No newline at end of file diff --git a/backend/routes/contactUsRouter.js b/backend/routes/contactUsRouter.js index 6cbc0fa..11e97f6 100644 --- a/backend/routes/contactUsRouter.js +++ b/backend/routes/contactUsRouter.js @@ -1,7 +1,6 @@ import express from 'express'; const router = express.Router(); -import { createContactUs } from "../controllers/contactusController.js"; +import { createContactUs} from "../controllers/contactusController.js"; router.post("/contactus", createContactUs); - export default router; \ No newline at end of file diff --git a/frontend/src/Pages/Herosection.jsx b/frontend/src/Pages/Herosection.jsx index a6e72a0..ce4edac 100644 --- a/frontend/src/Pages/Herosection.jsx +++ b/frontend/src/Pages/Herosection.jsx @@ -53,6 +53,9 @@ const Herosection = () => { const ContributorCLick = () => { navigate("/contributor"); // Navigates to the login page }; + const ContactClick = () => { + navigate("/contactus"); + }; return ( <> @@ -93,8 +96,21 @@ const Herosection = () => { */}

- +

+
+ phone-disconnected +

Contact Us

+
Date: Thu, 7 Nov 2024 22:11:40 +0530 Subject: [PATCH 4/8] login page done --- frontend/src/Pages/LoginPage.jsx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend/src/Pages/LoginPage.jsx b/frontend/src/Pages/LoginPage.jsx index 2ecad30..cc5b09d 100644 --- a/frontend/src/Pages/LoginPage.jsx +++ b/frontend/src/Pages/LoginPage.jsx @@ -190,20 +190,20 @@ const Login = () => { /> Sign in with Google - - - + {/* Don't have an account link */} +
+

+ Don't have an account?{" "} + +

+
- {/* Don't have an account link */} -

- Don't have an account?{" "} - -

+
); }; From 7a80621bef8065a6e2fb4dd413c2338f110d44df Mon Sep 17 00:00:00 2001 From: haseebzaki-07 Date: Fri, 8 Nov 2024 02:02:41 +0530 Subject: [PATCH 5/8] add email verification --- backend/controllers/authController.js | 87 +++++++++++++++++++++++++-- backend/models/User.js | 4 ++ backend/routes/authRoutes.js | 3 + 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/backend/controllers/authController.js b/backend/controllers/authController.js index 0963014..efd87a3 100644 --- a/backend/controllers/authController.js +++ b/backend/controllers/authController.js @@ -1,4 +1,6 @@ import User from '../models/User.js'; +import crypto from 'crypto'; // For generating OTP +import nodemailer from 'nodemailer'; import { hashPassword, comparePassword, generateToken, verifyToken, addCookie, getCookies, removeCookie } from '../utils/authFunctions.js'; export const registerUser = async (req, res) => { @@ -19,21 +21,30 @@ export const registerUser = async (req, res) => { return res.status(400).json({ error: 'User already exists' }); } + const otp = crypto.randomInt(100000, 999999).toString(); // Generate OTP (6 digits) + if (isGoogle == true) { const newUser = new User({ name, email, phoneNumber: phoneNumber ? phoneNumber : '', - password: '' + password: '', + otp: otp, + otpExpiry: Date.now() + 3600000, // OTP expires in 1 hour }); await newUser.save(); - const token = await generateToken(newUser._id); + await sendOtpEmail(email, otp); // Send OTP to the user's email + const token = await generateToken(newUser._id); addCookie(res, 'token', token); - return res.status(201).json({ message: 'User registered successfully', userId: newUser._id , token: token }); + return res.status(201).json({ + message: 'User registered successfully. Please check your email for the OTP to verify your email.', + userId: newUser._id, + token: token, + }); } else { const hashedPassword = await hashPassword(password); @@ -41,23 +52,87 @@ export const registerUser = async (req, res) => { name, email, phoneNumber: phoneNumber ? phoneNumber : '', - password: hashedPassword + password: hashedPassword, + otp: otp, + otpExpiry: Date.now() + 3600000, // OTP expires in 1 hour }); await newUser.save(); - const token = await generateToken(newUser._id); + await sendOtpEmail(email, otp); // Send OTP to the user's email + const token = await generateToken(newUser._id); addCookie(res, 'token', token); - res.status(201).json({ message: 'User registered successfully', userId: newUser._id, token: token }); + res.status(201).json({ + message: 'User registered successfully. Please check your email for the OTP to verify your email.', + userId: newUser._id, + token: token, + }); + } + } catch (error) { + console.error(error); + res.status(500).json({ error: error.message || 'Internal Server Error' }); + } +}; + +const sendOtpEmail = async (userEmail, otp) => { + try { + const transporter = nodemailer.createTransport({ + service: 'gmail', // Or use another email provider + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS, + }, + }); + + const mailOptions = { + from: process.env.EMAIL_USER, + to: userEmail, + subject: 'Email Verification - Station Sarthi', + text: `Your OTP for email verification is: ${otp}`, + }; + + await transporter.sendMail(mailOptions); + } catch (error) { + console.error('Error sending email:', error); + throw new Error('Failed to send OTP'); + } +}; + +export const verifyOtp = async (req, res) => { + try { + const { email, otp } = req.body; + + const user = await User.findOne({ email }); + if (!user) { + return res.status(404).json({ error: 'User not found' }); } + + // Check if OTP has expired + if (Date.now() > user.otpExpiry) { + return res.status(400).json({ error: 'OTP has expired' }); + } + + // Check if OTP is correct + if (user.otp !== otp) { + return res.status(400).json({ error: 'Invalid OTP' }); + } + + // OTP is correct, mark user as verified + user.isVerified = true; + user.otp = null; // Clear OTP after verification + user.otpExpiry = null; // Clear OTP expiry after verification + await user.save(); + + res.status(200).json({ message: 'Email verified successfully' }); } catch (error) { console.error(error); res.status(500).json({ error: error.message || 'Internal Server Error' }); } }; + export const loginUser = async (req, res) => { try { const { email, password } = req.body; diff --git a/backend/models/User.js b/backend/models/User.js index ca95f98..1fa0796 100644 --- a/backend/models/User.js +++ b/backend/models/User.js @@ -40,6 +40,10 @@ const userSchema = new Schema({ otpExpiry: { type: Date, // The OTP expiry timestamp required: false, + }, + isVerified: { + type: Boolean, + default: false, // This field will be set to true once the email is verified } }, { timestamps: true diff --git a/backend/routes/authRoutes.js b/backend/routes/authRoutes.js index 8f17bc1..50b0ed4 100644 --- a/backend/routes/authRoutes.js +++ b/backend/routes/authRoutes.js @@ -5,6 +5,7 @@ import { loginUser, logoutUser, verifyUser, + verifyOtp, } from "../controllers/authController.js"; import { createCloakroomBooking } from "../controllers/cloakroomController.js"; import { createWheelchairBooking } from "../controllers/WheelchairController.js"; @@ -24,6 +25,8 @@ const loginLimiter = rateLimit({ // Register route router.post("/register", registerUser); +router.post("/verify-otp", verifyOtp); + // Login route with rate limiter router.post("/login", loginLimiter, loginUser); From 4ef113c6156de0aff3072098e1b5f40815d28139 Mon Sep 17 00:00:00 2001 From: souvikpramanikgit Date: Fri, 8 Nov 2024 02:44:29 +0530 Subject: [PATCH 6/8] Add changes --- frontend/index.html | 355 ++++++++++++++++++++++---------------------- 1 file changed, 175 insertions(+), 180 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index ecee1ac..05a735e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,5 +1,6 @@ + @@ -10,18 +11,15 @@ onerror="handleError(this)" > --> -Station Saarthi - + 50% { + opacity: 1; + } + + + } + + -

Station Saarthi - Your Platform Guide

+
+

Station Saarthi - Your Platform Guide

+
- - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + - - - - - - -
- +
+ - - - - + + + +
- - - + + + \ No newline at end of file From 0a71f936f4b8a92c340dceb7ba37c489eb82112c Mon Sep 17 00:00:00 2001 From: Dhairya Gothi <142989448+dhairyagothi@users.noreply.github.com> Date: Fri, 8 Nov 2024 05:21:00 +0000 Subject: [PATCH 7/8] dg commit --- frontend/src/Pages/contributor.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/Pages/contributor.jsx b/frontend/src/Pages/contributor.jsx index 3b109b5..6ccc7d9 100644 --- a/frontend/src/Pages/contributor.jsx +++ b/frontend/src/Pages/contributor.jsx @@ -1,5 +1,7 @@ import React, { useEffect, useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; +import backicon from '../assets/svg/backicon.svg'; +import { useNavigate } from 'react-router-dom'; const ContributorCard = ({ login, avatar_url, html_url, contributions, type }) => ( { const [repoStats, setRepoStats] = useState({}); const [loading, setLoading] = useState(true); const [email, setEmail] = useState(''); + const navigate = useNavigate(); + const HomeClick = () => navigate('/'); useEffect(() => { document.title = 'Station Saarthi | Contributors'; @@ -97,6 +101,9 @@ const contributor = () => { return (
{/* Hero Section */} +
From ce932d2431a158b05ab59c00f2c2030ce6202aa2 Mon Sep 17 00:00:00 2001 From: haseebzaki-07 Date: Fri, 8 Nov 2024 11:49:24 +0530 Subject: [PATCH 8/8] add password reset --- backend/controllers/authController.js | 87 ++++++++++++++++++++++++--- backend/routes/authRoutes.js | 8 +++ 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/backend/controllers/authController.js b/backend/controllers/authController.js index efd87a3..37e67ce 100644 --- a/backend/controllers/authController.js +++ b/backend/controllers/authController.js @@ -3,6 +3,15 @@ import crypto from 'crypto'; // For generating OTP import nodemailer from 'nodemailer'; import { hashPassword, comparePassword, generateToken, verifyToken, addCookie, getCookies, removeCookie } from '../utils/authFunctions.js'; + +const transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS, + }, +}); + export const registerUser = async (req, res) => { try { const { name, email, phoneNumber, password, isGoogle } = req.body; @@ -78,13 +87,6 @@ export const registerUser = async (req, res) => { const sendOtpEmail = async (userEmail, otp) => { try { - const transporter = nodemailer.createTransport({ - service: 'gmail', // Or use another email provider - auth: { - user: process.env.EMAIL_USER, - pass: process.env.EMAIL_PASS, - }, - }); const mailOptions = { from: process.env.EMAIL_USER, @@ -165,6 +167,77 @@ export const loginUser = async (req, res) => { } }; +async function sendResetOtpEmail(email, otp) { + await transporter.sendMail({ + from: 'Station Sarthi ', + to: email, + subject: 'Password Reset OTP - Station Sarti', + text: `Your OTP for password reset is ${otp}. It will expire in 1 hour.` + }); +} + +// Route 1: Request Password Reset (Sends OTP) +export const requestPasswordReset = async (req, res) => { + try { + const { email } = req.body; + + if (!email) { + return res.status(400).json({ error: 'Email is required' }); + } + + const user = await User.findOne({ email }); + + if (!user) { + return res.status(400).json({ error: 'User with this email does not exist' }); + } + + const otp = crypto.randomInt(100000, 999999).toString(); // Generate OTP + user.otp = otp; + user.otpExpiry = Date.now() + 3600000; // OTP expires in 1 hour + await user.save(); + + await sendResetOtpEmail(email, otp); + + res.status(200).json({ message: 'OTP sent to email for password reset' }); + } catch (error) { + console.error(error); + res.status(500).json({ error: error.message || 'Internal Server Error' }); + } +}; + +// Route 2: Verify OTP and Reset Password +export const resetPassword = async (req, res) => { + try { + const { email, otp, newPassword } = req.body; + + if (!email || !otp || !newPassword) { + return res.status(400).json({ error: 'Email, OTP, and new password are required' }); + } + + const user = await User.findOne({ email }); + + if (!user) { + return res.status(400).json({ error: 'User does not exist' }); + } + + // Check if OTP is valid + if (user.otp !== otp || user.otpExpiry < Date.now()) { + return res.status(400).json({ error: 'Invalid or expired OTP' }); + } + + // Hash the new password and reset OTP fields + user.password = await hashPassword(newPassword); + user.otp = null; + user.otpExpiry = null; + await user.save(); + + res.status(200).json({ message: 'Password has been reset successfully' }); + } catch (error) { + console.error(error); + res.status(500).json({ error: error.message || 'Internal Server Error' }); + } +}; + export const logoutUser = async (req, res) => { try { removeCookie(res, 'token'); diff --git a/backend/routes/authRoutes.js b/backend/routes/authRoutes.js index 50b0ed4..ec31c37 100644 --- a/backend/routes/authRoutes.js +++ b/backend/routes/authRoutes.js @@ -6,6 +6,8 @@ import { logoutUser, verifyUser, verifyOtp, + requestPasswordReset, + resetPassword, } from "../controllers/authController.js"; import { createCloakroomBooking } from "../controllers/cloakroomController.js"; import { createWheelchairBooking } from "../controllers/WheelchairController.js"; @@ -30,6 +32,12 @@ router.post("/verify-otp", verifyOtp); // Login route with rate limiter router.post("/login", loginLimiter, loginUser); +// Route to request OTP for password reset +router.post('/request-password-reset', requestPasswordReset); + +// Route to reset password after OTP verification +router.post('/reset-password', resetPassword); + // Logout route router.post("/logout", verifyJWT, logoutUser);