initial commit

This commit is contained in:
Harikrishnan Gopal
2024-12-03 15:53:50 +05:30
commit 956cf14c53
26 changed files with 22820 additions and 0 deletions

2
server/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules
.env

12
server/ConnectionDb.js Normal file
View File

@@ -0,0 +1,12 @@
const mongoose = require("mongoose");
require("dotenv").config();
exports.connectdb = () => {
mongoose.connect(process.env.mongoURI);
};
const db = mongoose.connection;
db.on("error", console.error.bind("Connection Error!"));
db.once("open", function () {
console.log("Connection Established!!!");
});

56
server/config/passport.js Normal file
View File

@@ -0,0 +1,56 @@
const passport = require("passport");
const User = require("../models/User");
require("dotenv").config();
const GoogleStrategy = require("passport-google-oauth20").Strategy;
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:8080/auth/google/callback",
scope: ["profile", "email", "displayName"],
},
async (accessToken, refreshToken, profile, done) => {
try {
// Check if a user with the same email already exists
let user = await User.findOne({ email: profile.emails[0].value });
if (user) {
// If the user exists, update their Google ID and profile information
user.googleId = profile.id;
user.username = profile.displayName;
user.profilePicture = profile.photos[0].value;
await user.save();
return done(null, user);
} else {
// If the user doesn't exist, create a new user
const newUser = new User({
googleId: profile.id,
username: profile.displayName,
email: profile.emails[0].value,
profilePicture: profile.photos[0].value,
});
user = await newUser.save();
return done(null, user);
}
} catch (error) {
return done(error, false);
}
}
)
);
passport.serializeUser((user, done) => {
done(null, user.id); // Use user id for serialization
});
passport.deserializeUser(async (id, done) => {
try {
const user = await User.findById(id); // Fetch user by id from MongoDB
done(null, user); // Pass the user object to the next middleware
} catch (error) {
done(error, false);
}
});

27
server/models/User.js Normal file
View File

@@ -0,0 +1,27 @@
const mongoose = require("mongoose");
const passportLocalMongoose = require("passport-local-mongoose");
const UserSchema = new mongoose.Schema(
{
googleId: String,
username: String,
email: String,
password: String,
profilePicture: String,
resetPasswordToken: {
type: String,
default: null,
},
resetPasswordExpires: {
type: Date,
default: null,
},
},
{
timestamps: true,
}
);
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);

2266
server/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
server/package.json Normal file
View File

@@ -0,0 +1,42 @@
{
"name": "server",
"version": "1.0.0",
"description": "appointment_to_examiner",
"main": "sever.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build" : "npm install --prefix ../client && npm run build --prefix ../client && npm install",
"start": "nodemon server.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/hk151109/appointment_to_examiner.git"
},
"author": "Harikrishnan",
"license": "ISC",
"bugs": {
"url": "https://github.com/hk151109/appointment_to_examiner/issues"
},
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"connect-mongo": "^5.1.0",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-session": "^1.18.0",
"googleapis": "^134.0.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.3.1",
"mongoose-findorcreate": "^4.0.0",
"nodemailer": "^6.9.13",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
"passport-local": "^1.0.0",
"passport-local-mongoose": "^8.0.0"
},
"devDependencies": {
"nodemon": "^3.1.0"
}
}

124
server/routes/authRoutes.js Normal file
View File

@@ -0,0 +1,124 @@
const express = require("express");
const router = express.Router();
const bcrypt = require("bcryptjs");
const User = require("../models/User");
const crypto = require("crypto");
const nodemailer = require("nodemailer");
const { google } = require("googleapis");
require("dotenv").config();
// Set up Google OAuth2 credentials
const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
"https://developers.google.com/oauthplayground" // Redirect URI
);
oauth2Client.setCredentials({
refresh_token: process.env.GOOGLE_REFRESH_TOKEN,
});
// Nodemailer transporter using Google OAuth2
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
type: "OAuth2",
user: process.env.EMAIL_USERNAME,
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
refreshToken: process.env.GOOGLE_REFRESH_TOKEN,
accessToken: async () => {
const accessToken = await getAccessToken();
return accessToken;
},
},
});
// Function to refresh OAuth2 token
async function getAccessToken() {
try {
const { token } = await oauth2Client.getAccessToken();
return token;
} catch (error) {
console.error("Error refreshing access token:", error);
throw error;
}
}
// Forgot Password
router.post("/forgot-password", async (req, res) => {
try {
const { email } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(404).json({ message: "User not found" });
}
// Generate reset token
const resetToken = crypto.randomBytes(20).toString("hex");
user.resetPasswordToken = resetToken;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
await user.save();
// Send reset email
const mailOptions = {
from: `"MERN Auth App" <${process.env.EMAIL_USERNAME}>`,
to: email,
subject: "Password Reset Verification",
html: `
<p>Hi there,</p>
<br>
<p>We received a request to reset the password for your account. To proceed with the password reset, please click on the link below:</p>
<p>Click here : <a href="http://localhost:3000/ResetPw/${resetToken}">Reset Password</a></p>
<br>
<p>Please ensure you use this link within the next 1 hour as it will expire after that for security reasons.</p>
<p>If you didn't request a password reset, please ignore this email.</p>
<br>
<p>Best regards,</p>
<p>MERN Auth App Team</p>
`,
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error("Error sending email:", error);
return res.status(500).json({ message: "Error sending email" });
}
console.log("Email sent:", info.response);
res.status(200).json({ message: "Reset password email sent" });
});
} catch (error) {
console.error("Forgot password error:", error);
res.status(500).json({ message: "Internal server error" });
}
});
// Reset Password
router.post("/reset-password", async (req, res) => {
try {
const { resetToken, newPassword } = req.body;
const user = await User.findOne({
resetPasswordToken: resetToken,
resetPasswordExpires: { $gt: Date.now() },
});
if (!user) {
return res.status(400).json({ message: "Invalid or expired token" });
}
// Reset password
const hashedPassword = await bcrypt.hash(newPassword, 10);
user.password = hashedPassword;
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
await user.save();
res.status(200).json({ message: "Password reset successful" });
} catch (error) {
console.error("Reset password error:", error);
res.status(500).json({ message: "Internal server error" });
}
});
module.exports = router;

188
server/server.js Normal file
View File

@@ -0,0 +1,188 @@
const express = require("express");
const User = require("./models/User");
const cors = require("cors");
const passport = require("passport");
const session = require("express-session");
const bodyParser = require("body-parser");
const bcrypt = require("bcryptjs");
const LocalStrategy = require("passport-local").Strategy;
const PasswordRouter = require("./routes/authRoutes");
const crypto = require("crypto");
const jwt = require("jsonwebtoken");
const path = require("path");
const { connectdb } = require("./ConnectionDb");
connectdb();
const app = express();
// Middleware
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(
session({
secret: "secret",
resave: false,
saveUninitialized: false,
})
);
app.use(passport.initialize());
app.use(passport.session());
// CORS configuration
app.use(
cors({
origin: "http://localhost:3000",
credentials: true,
})
);
// Passport configuration
require("./config/passport");
passport.use(
new LocalStrategy(
{ usernameField: "email" },
async (email, password, done) => {
try {
const user = await User.findOne({ email });
if (!user) {
return done(null, false, { message: "Incorrect email" });
}
const isMatch = await bcrypt.compare(password, user.password);
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Incorrect password" });
}
} catch (error) {
return done(error);
}
}
)
);
passport.serializeUser((user, done) => {
done(null, user.id); // Store user ID in the session
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
// Routes
app.use("/password", PasswordRouter);
app.get(
"/auth/google",
passport.authenticate("google", { scope: ["profile", "email"] })
);
app.get(
"/auth/google/callback",
passport.authenticate("google", { failureRedirect: "/" }),
function (req, res) {
res.redirect("http://localhost:3000/Home");
}
);
app.post("/api/register", async (req, res) => {
try {
const { username, email, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
let user = await User.findOne({ email });
if (user) {
if (user.googleId && user.password) {
return res.status(400).json({ message: "User already exists" });
}
if (!user.googleId && user.password) {
return res.status(400).json({ message: "User already exists" });
}
if (user.googleId && !user.password) {
user.password = hashedPassword;
await user.save();
}
} else {
user = new User({ username, email, password: hashedPassword });
await user.save();
}
req.login(user, (err) => {
if (err) {
console.error("Error logging in user after registration:", err);
return res.status(500).send("Internal server error");
}
return res.status(200).json({
message: "Registered and logged in successfully",
user,
});
});
} catch (error) {
console.error("Error registering user:", error);
res.status(400).send("Registration failed");
}
});
app.post("/api/login", (req, res, next) => {
passport.authenticate("local", (err, user, info) => {
if (err) {
return res.status(500).json({ message: "Internal server error" });
}
if (!user) {
return res.status(401).json({ message: "Incorrect email or password" });
}
req.logIn(user, (err) => {
if (err) {
return res.status(500).json({ message: "Internal server error" });
}
return res.status(200).json({ message: "Login successful", user });
});
})(req, res, next);
});
app.get("/auth/logout", function (req, res) {
req.logout((err) => {
if (err) {
console.log(err);
return res.status(500).json({ message: "Error logging out" });
}
req.session.destroy(function (err) {
if (err) {
console.log(err);
return res.status(500).json({ message: "Error destroying session" });
}
res.json({ message: "Logout successful" });
});
});
});
app.get("/api/user/profile", async (req, res) => {
try {
if (req.user) {
return res.json({ user: req.user });
} else {
return res.status(401).json({ error: "Unauthorized" });
}
} catch (error) {
console.error("Error fetching user data:", error);
res.status(500).json({ message: "Internal server error" });
}
});
// Serve static files
app.use(express.static(path.join(__dirname, "../client/build")));
// Catch-all route to serve React app
app.get("*", (req, res) =>
res.sendFile(path.join(__dirname, "../client/build/index.html"))
);
const Port = 8080;
app.listen(Port, () => {
console.log(`Server is Running at port ${Port}`);
});