-
Notifications
You must be signed in to change notification settings - Fork 133
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7254844
commit 155b9d5
Showing
2,126 changed files
with
330,925 additions
and
0 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
PORT_SERVER=5000 | ||
MONGO_URI=mongodb://localhost:27017/config | ||
JWT_SECRET_KEY=haweihfrbt4t45_ere@etw43 | ||
CLIENT_URL=http://localhost:3000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
import bcryptjs from "bcryptjs"; | ||
import crypto from "crypto"; | ||
|
||
import { generateTokenAndSetCookie } from "../utils/generateTokenAndSetCookie.js"; | ||
import { | ||
sendPasswordResetEmail, | ||
sendResetSuccessEmail, | ||
sendVerificationEmail, | ||
sendWelcomeEmail, | ||
} from "../mailtrap/emails.js"; | ||
import { User } from "../models/user.model.js"; | ||
|
||
export const signup = async (req, res) => { | ||
const { email, password, name } = req.body; | ||
|
||
try { | ||
if (!email || !password || !name) { | ||
throw new Error("All fields are required"); | ||
} | ||
|
||
const userAlreadyExists = await User.findOne({ email }); | ||
console.log("userAlreadyExists", userAlreadyExists); | ||
|
||
if (userAlreadyExists) { | ||
return res.status(400).json({ success: false, message: "User already exists" }); | ||
} | ||
|
||
const hashedPassword = await bcryptjs.hash(password, 10); | ||
const verificationToken = Math.floor(100000 + Math.random() * 900000).toString(); | ||
|
||
const user = new User({ | ||
email, | ||
password: hashedPassword, | ||
name, | ||
verificationToken, | ||
verificationTokenExpiresAt: Date.now() + 24 * 60 * 60 * 1000, // 24 hours | ||
}); | ||
|
||
await user.save(); | ||
|
||
// jwt | ||
generateTokenAndSetCookie(res, user._id); | ||
|
||
await sendVerificationEmail(user.email, verificationToken); | ||
|
||
res.status(201).json({ | ||
success: true, | ||
message: "User created successfully", | ||
user: { | ||
...user._doc, | ||
password: undefined, | ||
}, | ||
}); | ||
} catch (error) { | ||
res.status(400).json({ success: false, message: error.message }); | ||
} | ||
}; | ||
|
||
export const verifyEmail = async (req, res) => { | ||
const { code } = req.body; | ||
try { | ||
const user = await User.findOne({ | ||
verificationToken: code, | ||
verificationTokenExpiresAt: { $gt: Date.now() }, | ||
}); | ||
|
||
if (!user) { | ||
return res.status(400).json({ success: false, message: "Invalid or expired verification code" }); | ||
} | ||
|
||
user.isVerified = true; | ||
user.verificationToken = undefined; | ||
user.verificationTokenExpiresAt = undefined; | ||
await user.save(); | ||
|
||
await sendWelcomeEmail(user.email, user.name); | ||
|
||
res.status(200).json({ | ||
success: true, | ||
message: "Email verified successfully", | ||
user: { | ||
...user._doc, | ||
password: undefined, | ||
}, | ||
}); | ||
} catch (error) { | ||
console.log("error in verifyEmail ", error); | ||
res.status(500).json({ success: false, message: "Server error" }); | ||
} | ||
}; | ||
|
||
export const login = async (req, res) => { | ||
const { email, password } = req.body; | ||
try { | ||
const user = await User.findOne({ email }); | ||
if (!user) { | ||
return res.status(400).json({ success: false, message: "Invalid credentials" }); | ||
} | ||
const isPasswordValid = await bcryptjs.compare(password, user.password); | ||
if (!isPasswordValid) { | ||
return res.status(400).json({ success: false, message: "Invalid credentials" }); | ||
} | ||
|
||
generateTokenAndSetCookie(res, user._id); | ||
|
||
user.lastLogin = new Date(); | ||
await user.save(); | ||
|
||
res.status(200).json({ | ||
success: true, | ||
message: "Logged in successfully", | ||
user: { | ||
...user._doc, | ||
password: undefined, | ||
}, | ||
}); | ||
} catch (error) { | ||
console.log("Error in login ", error); | ||
res.status(400).json({ success: false, message: error.message }); | ||
} | ||
}; | ||
|
||
export const logout = async (req, res) => { | ||
res.clearCookie("token"); | ||
res.status(200).json({ success: true, message: "Logged out successfully" }); | ||
}; | ||
|
||
export const forgotPassword = async (req, res) => { | ||
const { email } = req.body; | ||
try { | ||
const user = await User.findOne({ email }); | ||
|
||
if (!user) { | ||
return res.status(400).json({ success: false, message: "User not found" }); | ||
} | ||
|
||
// Generate reset token | ||
const resetToken = crypto.randomBytes(20).toString("hex"); | ||
const resetTokenExpiresAt = Date.now() + 1 * 60 * 60 * 1000; // 1 hour | ||
|
||
user.resetPasswordToken = resetToken; | ||
user.resetPasswordExpiresAt = resetTokenExpiresAt; | ||
|
||
await user.save(); | ||
|
||
// send email | ||
await sendPasswordResetEmail(user.email, `${process.env.CLIENT_URL}/reset-password/${resetToken}`); | ||
|
||
res.status(200).json({ success: true, message: "Password reset link sent to your email" }); | ||
} catch (error) { | ||
console.log("Error in forgotPassword ", error); | ||
res.status(400).json({ success: false, message: error.message }); | ||
} | ||
}; | ||
|
||
export const resetPassword = async (req, res) => { | ||
try { | ||
const { token } = req.params; | ||
const { password } = req.body; | ||
|
||
const user = await User.findOne({ | ||
resetPasswordToken: token, | ||
resetPasswordExpiresAt: { $gt: Date.now() }, | ||
}); | ||
|
||
if (!user) { | ||
return res.status(400).json({ success: false, message: "Invalid or expired reset token" }); | ||
} | ||
|
||
// update password | ||
const hashedPassword = await bcryptjs.hash(password, 10); | ||
|
||
user.password = hashedPassword; | ||
user.resetPasswordToken = undefined; | ||
user.resetPasswordExpiresAt = undefined; | ||
await user.save(); | ||
|
||
await sendResetSuccessEmail(user.email); | ||
|
||
res.status(200).json({ success: true, message: "Password reset successful" }); | ||
} catch (error) { | ||
console.log("Error in resetPassword ", error); | ||
res.status(400).json({ success: false, message: error.message }); | ||
} | ||
}; | ||
|
||
export const checkAuth = async (req, res) => { | ||
try { | ||
const user = await User.findById(req.userId).select("-password"); | ||
if (!user) { | ||
return res.status(400).json({ success: false, message: "User not found" }); | ||
} | ||
|
||
res.status(200).json({ success: true, user }); | ||
} catch (error) { | ||
console.log("Error in checkAuth ", error); | ||
res.status(400).json({ success: false, message: error.message }); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import mongoose from "mongoose"; | ||
|
||
export const connectDB = async () => { | ||
try { | ||
console.log("mongo_uri: ", process.env.MONGO_URI); | ||
const conn = await mongoose.connect(process.env.MONGO_URI); | ||
console.log(`MongoDB Connected: ${conn.connection.host}`); | ||
} catch (error) { | ||
console.log("Error connection to MongoDB: ", error.message); | ||
process.exit(1); // 1 is failure, 0 status code is success | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import express from "express"; | ||
import dotenv from "dotenv"; | ||
import cors from "cors"; | ||
import cookieParser from "cookie-parser"; | ||
import path from "path"; | ||
|
||
import { connectDB } from "./db/connectDB.js"; | ||
|
||
import authRoutes from "./routes/auth.route.js"; | ||
|
||
dotenv.config(); | ||
|
||
const app = express(); | ||
const PORT = process.env.PORT || 5000; | ||
const __dirname = path.resolve(); | ||
|
||
app.use(cors({ origin: "http://localhost:5173", credentials: true })); | ||
|
||
app.use(express.json()); // allows us to parse incoming requests:req.body | ||
app.use(cookieParser()); // allows us to parse incoming cookies | ||
|
||
app.use("/api/auth", authRoutes); | ||
|
||
if (process.env.NODE_ENV === "production") { | ||
app.use(express.static(path.join(__dirname, "/frontend/dist"))); | ||
|
||
app.get("*", (req, res) => { | ||
res.sendFile(path.resolve(__dirname, "frontend", "dist", "index.html")); | ||
}); | ||
} | ||
|
||
app.listen(PORT, () => { | ||
connectDB(); | ||
console.log("Server is running on port: ", PORT); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
export const VERIFICATION_EMAIL_TEMPLATE = ` | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Verify Your Email</title> | ||
</head> | ||
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;"> | ||
<div style="background: linear-gradient(to right, #4CAF50, #45a049); padding: 20px; text-align: center;"> | ||
<h1 style="color: white; margin: 0;">Verify Your Email</h1> | ||
</div> | ||
<div style="background-color: #f9f9f9; padding: 20px; border-radius: 0 0 5px 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);"> | ||
<p>Hello,</p> | ||
<p>Thank you for signing up! Your verification code is:</p> | ||
<div style="text-align: center; margin: 30px 0;"> | ||
<span style="font-size: 32px; font-weight: bold; letter-spacing: 5px; color: #4CAF50;">{verificationCode}</span> | ||
</div> | ||
<p>Enter this code on the verification page to complete your registration.</p> | ||
<p>This code will expire in 15 minutes for security reasons.</p> | ||
<p>If you didn't create an account with us, please ignore this email.</p> | ||
<p>Best regards,<br>Your App Team</p> | ||
</div> | ||
<div style="text-align: center; margin-top: 20px; color: #888; font-size: 0.8em;"> | ||
<p>This is an automated message, please do not reply to this email.</p> | ||
</div> | ||
</body> | ||
</html> | ||
`; | ||
|
||
export const PASSWORD_RESET_SUCCESS_TEMPLATE = ` | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Password Reset Successful</title> | ||
</head> | ||
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;"> | ||
<div style="background: linear-gradient(to right, #4CAF50, #45a049); padding: 20px; text-align: center;"> | ||
<h1 style="color: white; margin: 0;">Password Reset Successful</h1> | ||
</div> | ||
<div style="background-color: #f9f9f9; padding: 20px; border-radius: 0 0 5px 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);"> | ||
<p>Hello,</p> | ||
<p>We're writing to confirm that your password has been successfully reset.</p> | ||
<div style="text-align: center; margin: 30px 0;"> | ||
<div style="background-color: #4CAF50; color: white; width: 50px; height: 50px; line-height: 50px; border-radius: 50%; display: inline-block; font-size: 30px;"> | ||
✓ | ||
</div> | ||
</div> | ||
<p>If you did not initiate this password reset, please contact our support team immediately.</p> | ||
<p>For security reasons, we recommend that you:</p> | ||
<ul> | ||
<li>Use a strong, unique password</li> | ||
<li>Enable two-factor authentication if available</li> | ||
<li>Avoid using the same password across multiple sites</li> | ||
</ul> | ||
<p>Thank you for helping us keep your account secure.</p> | ||
<p>Best regards,<br>Your App Team</p> | ||
</div> | ||
<div style="text-align: center; margin-top: 20px; color: #888; font-size: 0.8em;"> | ||
<p>This is an automated message, please do not reply to this email.</p> | ||
</div> | ||
</body> | ||
</html> | ||
`; | ||
|
||
export const PASSWORD_RESET_REQUEST_TEMPLATE = ` | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Reset Your Password</title> | ||
</head> | ||
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;"> | ||
<div style="background: linear-gradient(to right, #4CAF50, #45a049); padding: 20px; text-align: center;"> | ||
<h1 style="color: white; margin: 0;">Password Reset</h1> | ||
</div> | ||
<div style="background-color: #f9f9f9; padding: 20px; border-radius: 0 0 5px 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);"> | ||
<p>Hello,</p> | ||
<p>We received a request to reset your password. If you didn't make this request, please ignore this email.</p> | ||
<p>To reset your password, click the button below:</p> | ||
<div style="text-align: center; margin: 30px 0;"> | ||
<a href="{resetURL}" style="background-color: #4CAF50; color: white; padding: 12px 20px; text-decoration: none; border-radius: 5px; font-weight: bold;">Reset Password</a> | ||
</div> | ||
<p>This link will expire in 1 hour for security reasons.</p> | ||
<p>Best regards,<br>Your App Team</p> | ||
</div> | ||
<div style="text-align: center; margin-top: 20px; color: #888; font-size: 0.8em;"> | ||
<p>This is an automated message, please do not reply to this email.</p> | ||
</div> | ||
</body> | ||
</html> | ||
`; |
Oops, something went wrong.