restored everything after nuking the repo
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
PORT=5001
|
||||
JWT_SECRET="shhhkeepitasecret"
|
||||
MONGODB_URI="mongodb://localhost:27017/chat-app"
|
||||
Generated
+1762
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"dev": "nodemon src/index.js",
|
||||
"start": "node src/index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"type": "module",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bcryptjs": "^2.4.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.21.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mongoose": "^8.8.1",
|
||||
"multer": "^2.0.2",
|
||||
"socket.io": "^4.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.7"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import { generateToken } from "../lib/utils.js";
|
||||
import User from "../models/user.model.js";
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
export const signup = async (req, res) => {
|
||||
const { fullName, email, password } = req.body;
|
||||
try {
|
||||
if (!fullName || !email || !password) {
|
||||
return res.status(400).json({ message: "All fields are required" });
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Password must be at least 6 characters" });
|
||||
}
|
||||
|
||||
const user = await User.findOne({ email });
|
||||
|
||||
if (user) return res.status(400).json({ message: "Email already exists" });
|
||||
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
const hashedPassword = await bcrypt.hash(password, salt);
|
||||
|
||||
const newUser = new User({
|
||||
fullName,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
});
|
||||
|
||||
if (newUser) {
|
||||
// generate jwt token here
|
||||
generateToken(newUser._id, res);
|
||||
await newUser.save();
|
||||
|
||||
res.status(201).json({
|
||||
_id: newUser._id,
|
||||
fullName: newUser.fullName,
|
||||
email: newUser.email,
|
||||
profilePic: newUser.profilePic,
|
||||
});
|
||||
} else {
|
||||
res.status(400).json({ message: "Invalid user data" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error in signup controller", error.message);
|
||||
res.status(500).json({ message: "Internal 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({ message: "Invalid credentials" });
|
||||
}
|
||||
|
||||
const isPasswordCorrect = await bcrypt.compare(password, user.password);
|
||||
if (!isPasswordCorrect) {
|
||||
return res.status(400).json({ message: "Invalid credentials" });
|
||||
}
|
||||
|
||||
generateToken(user._id, res);
|
||||
|
||||
res.status(200).json({
|
||||
_id: user._id,
|
||||
fullName: user.fullName,
|
||||
email: user.email,
|
||||
profilePic: user.profilePic,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error in login controller", error.message);
|
||||
res.status(500).json({ message: "Internal Server Error" });
|
||||
}
|
||||
};
|
||||
|
||||
export const logout = (req, res) => {
|
||||
try {
|
||||
res.cookie("jwt", "", { maxAge: 0 });
|
||||
res.status(200).json({ message: "Logged out successfully" });
|
||||
} catch (error) {
|
||||
console.log("Error in logout controller", error.message);
|
||||
res.status(500).json({ message: "Internal Server Error" });
|
||||
}
|
||||
};
|
||||
|
||||
export const updateProfile = async (req, res) => {
|
||||
try {
|
||||
const profilePic = req.file;
|
||||
const userId = req.user._id;
|
||||
|
||||
if (!profilePic) {
|
||||
return res.status(400).json({ message: "Profile pic is required" });
|
||||
}
|
||||
|
||||
console.log(profilePic);
|
||||
const updatedUser = await User.findByIdAndUpdate(
|
||||
userId,
|
||||
{
|
||||
profilePic: `http://localhost:${process.env.PORT}/` + profilePic.path,
|
||||
},
|
||||
{ new: true },
|
||||
);
|
||||
|
||||
res.status(200).json(updatedUser);
|
||||
} catch (error) {
|
||||
console.log("error in update profile:", error);
|
||||
res.status(500).json({ message: "Internal server error" });
|
||||
}
|
||||
};
|
||||
|
||||
export const checkAuth = (req, res) => {
|
||||
try {
|
||||
res.status(200).json(req.user);
|
||||
} catch (error) {
|
||||
console.log("Error in checkAuth controller", error.message);
|
||||
res.status(500).json({ message: "Internal Server Error" });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import User from "../models/user.model.js";
|
||||
import Message from "../models/message.model.js";
|
||||
|
||||
import { getReceiverSocketId, io } from "../lib/socket.js";
|
||||
|
||||
export const getUsersForSidebar = async (req, res) => {
|
||||
try {
|
||||
const loggedInUserId = req.user._id;
|
||||
const filteredUsers = await User.find({
|
||||
_id: { $ne: loggedInUserId },
|
||||
}).select("-password");
|
||||
|
||||
res.status(200).json(filteredUsers);
|
||||
} catch (error) {
|
||||
console.error("Error in getUsersForSidebar: ", error.message);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
};
|
||||
|
||||
export const getMessages = async (req, res) => {
|
||||
try {
|
||||
const { id: userToChatId } = req.params;
|
||||
const myId = req.user._id;
|
||||
|
||||
const messages = await Message.find({
|
||||
$or: [
|
||||
{ senderId: myId, receiverId: userToChatId },
|
||||
{ senderId: userToChatId, receiverId: myId },
|
||||
],
|
||||
});
|
||||
|
||||
res.status(200).json(messages);
|
||||
} catch (error) {
|
||||
console.log("Error in getMessages controller: ", error.message);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
};
|
||||
|
||||
export const sendMessage = async (req, res) => {
|
||||
try {
|
||||
const { text, image } = req.body;
|
||||
const { id: receiverId } = req.params;
|
||||
const senderId = req.user._id;
|
||||
|
||||
let imageUrl;
|
||||
if (image) {
|
||||
//TODO image
|
||||
}
|
||||
|
||||
const newMessage = new Message({
|
||||
senderId,
|
||||
receiverId,
|
||||
text,
|
||||
image: imageUrl,
|
||||
});
|
||||
|
||||
await newMessage.save();
|
||||
|
||||
const receiverSocketId = getReceiverSocketId(receiverId);
|
||||
if (receiverSocketId) {
|
||||
io.to(receiverSocketId).emit("newMessage", newMessage);
|
||||
}
|
||||
|
||||
res.status(201).json(newMessage);
|
||||
} catch (error) {
|
||||
console.log("Error in sendMessage controller: ", error.message);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
import express from "express";
|
||||
import dotenv from "dotenv";
|
||||
import cookieParser from "cookie-parser";
|
||||
import cors from "cors";
|
||||
import path from "path";
|
||||
|
||||
import { connectDB } from "./lib/db.js";
|
||||
|
||||
import authRoutes from "./routes/auth.route.js";
|
||||
import messageRoutes from "./routes/message.route.js";
|
||||
import { app, server } from "./lib/socket.js";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const PORT = process.env.PORT;
|
||||
const __dirname = path.resolve();
|
||||
|
||||
console.log(path.join(__dirname, "uploads"));
|
||||
app.use("/uploads", express.static(path.join(__dirname, "uploads")));
|
||||
app.use(express.json());
|
||||
app.use(cookieParser());
|
||||
app.use(
|
||||
cors({
|
||||
origin: "http://localhost:5173",
|
||||
credentials: true,
|
||||
}),
|
||||
);
|
||||
|
||||
app.use("/api/auth", authRoutes);
|
||||
app.use("/api/messages", messageRoutes);
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
app.use(express.static(path.join(__dirname, "../frontend/dist")));
|
||||
|
||||
app.get("*", (req, res) => {
|
||||
res.sendFile(path.join(__dirname, "../frontend", "dist", "index.html"));
|
||||
});
|
||||
}
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log("server is running on PORT:" + PORT);
|
||||
connectDB();
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
export const connectDB = async () => {
|
||||
try {
|
||||
const conn = await mongoose.connect(process.env.MONGODB_URI);
|
||||
console.log(`MongoDB connected: ${conn.connection.host}`);
|
||||
} catch (error) {
|
||||
console.log("MongoDB connection error:", error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import multer from "multer";
|
||||
import path from "path";
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
cb(null, "uploads/"); // make sure this folder exists
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
|
||||
const ext = path.extname(file.originalname);
|
||||
cb(null, file.fieldname + "-" + uniqueSuffix + ext);
|
||||
},
|
||||
});
|
||||
|
||||
export const upload = multer({ storage: storage });
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Server } from "socket.io";
|
||||
import http from "http";
|
||||
import express from "express";
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
origin: ["http://localhost:5173"],
|
||||
},
|
||||
});
|
||||
|
||||
export function getReceiverSocketId(userId) {
|
||||
return userSocketMap[userId];
|
||||
}
|
||||
|
||||
// used to store online users
|
||||
const userSocketMap = {}; // {userId: socketId}
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
console.log("A user connected", socket.id);
|
||||
|
||||
const userId = socket.handshake.query.userId;
|
||||
if (userId) userSocketMap[userId] = socket.id;
|
||||
|
||||
// io.emit() is used to send events to all the connected clients
|
||||
io.emit("getOnlineUsers", Object.keys(userSocketMap));
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
console.log("A user disconnected", socket.id);
|
||||
delete userSocketMap[userId];
|
||||
io.emit("getOnlineUsers", Object.keys(userSocketMap));
|
||||
});
|
||||
});
|
||||
|
||||
export { io, app, server };
|
||||
@@ -0,0 +1,16 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
export const generateToken = (userId, res) => {
|
||||
const token = jwt.sign({ userId }, process.env.JWT_SECRET, {
|
||||
expiresIn: "7d",
|
||||
});
|
||||
|
||||
res.cookie("jwt", token, {
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000, // MS
|
||||
httpOnly: true, // prevent XSS attacks cross-site scripting attacks
|
||||
sameSite: "strict", // CSRF attacks cross-site request forgery attacks
|
||||
secure: process.env.NODE_ENV !== "development",
|
||||
});
|
||||
|
||||
return token;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import User from "../models/user.model.js";
|
||||
|
||||
export const protectRoute = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.cookies.jwt;
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({ message: "Unauthorized - No Token Provided" });
|
||||
}
|
||||
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
|
||||
if (!decoded) {
|
||||
return res.status(401).json({ message: "Unauthorized - Invalid Token" });
|
||||
}
|
||||
|
||||
const user = await User.findById(decoded.userId).select("-password");
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "User not found" });
|
||||
}
|
||||
|
||||
req.user = user;
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
console.log("Error in protectRoute middleware: ", error.message);
|
||||
res.status(500).json({ message: "Internal server error" });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
const messageSchema = new mongoose.Schema(
|
||||
{
|
||||
senderId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
required: true,
|
||||
},
|
||||
receiverId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
required: true,
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
},
|
||||
image: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
const Message = mongoose.model("Message", messageSchema);
|
||||
|
||||
export default Message;
|
||||
@@ -0,0 +1,29 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
const userSchema = new mongoose.Schema(
|
||||
{
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
fullName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true,
|
||||
minlength: 6,
|
||||
},
|
||||
profilePic: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
const User = mongoose.model("User", userSchema);
|
||||
|
||||
export default User;
|
||||
@@ -0,0 +1,27 @@
|
||||
import express from "express";
|
||||
import {
|
||||
checkAuth,
|
||||
login,
|
||||
logout,
|
||||
signup,
|
||||
updateProfile,
|
||||
} from "../controllers/auth.controller.js";
|
||||
import { protectRoute } from "../middleware/auth.middleware.js";
|
||||
import { upload } from "../lib/fileStorage.js";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post("/signup", signup);
|
||||
router.post("/login", login);
|
||||
router.post("/logout", logout);
|
||||
|
||||
router.put(
|
||||
"/update-profile",
|
||||
protectRoute,
|
||||
upload.single("image"),
|
||||
updateProfile,
|
||||
);
|
||||
|
||||
router.get("/check", protectRoute, checkAuth);
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,12 @@
|
||||
import express from "express";
|
||||
import { protectRoute } from "../middleware/auth.middleware.js";
|
||||
import { getMessages, getUsersForSidebar, sendMessage } from "../controllers/message.controller.js";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/users", protectRoute, getUsersForSidebar);
|
||||
router.get("/:id", protectRoute, getMessages);
|
||||
|
||||
router.post("/send/:id", protectRoute, sendMessage);
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,115 @@
|
||||
import { config } from "dotenv";
|
||||
import { connectDB } from "../lib/db.js";
|
||||
import User from "../models/user.model.js";
|
||||
|
||||
config();
|
||||
|
||||
const seedUsers = [
|
||||
// Female Users
|
||||
{
|
||||
email: "emma.thompson@example.com",
|
||||
fullName: "Emma Thompson",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/women/1.jpg",
|
||||
},
|
||||
{
|
||||
email: "olivia.miller@example.com",
|
||||
fullName: "Olivia Miller",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/women/2.jpg",
|
||||
},
|
||||
{
|
||||
email: "sophia.davis@example.com",
|
||||
fullName: "Sophia Davis",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/women/3.jpg",
|
||||
},
|
||||
{
|
||||
email: "ava.wilson@example.com",
|
||||
fullName: "Ava Wilson",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/women/4.jpg",
|
||||
},
|
||||
{
|
||||
email: "isabella.brown@example.com",
|
||||
fullName: "Isabella Brown",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/women/5.jpg",
|
||||
},
|
||||
{
|
||||
email: "mia.johnson@example.com",
|
||||
fullName: "Mia Johnson",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/women/6.jpg",
|
||||
},
|
||||
{
|
||||
email: "charlotte.williams@example.com",
|
||||
fullName: "Charlotte Williams",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/women/7.jpg",
|
||||
},
|
||||
{
|
||||
email: "amelia.garcia@example.com",
|
||||
fullName: "Amelia Garcia",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/women/8.jpg",
|
||||
},
|
||||
|
||||
// Male Users
|
||||
{
|
||||
email: "james.anderson@example.com",
|
||||
fullName: "James Anderson",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/men/1.jpg",
|
||||
},
|
||||
{
|
||||
email: "william.clark@example.com",
|
||||
fullName: "William Clark",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/men/2.jpg",
|
||||
},
|
||||
{
|
||||
email: "benjamin.taylor@example.com",
|
||||
fullName: "Benjamin Taylor",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/men/3.jpg",
|
||||
},
|
||||
{
|
||||
email: "lucas.moore@example.com",
|
||||
fullName: "Lucas Moore",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/men/4.jpg",
|
||||
},
|
||||
{
|
||||
email: "henry.jackson@example.com",
|
||||
fullName: "Henry Jackson",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/men/5.jpg",
|
||||
},
|
||||
{
|
||||
email: "alexander.martin@example.com",
|
||||
fullName: "Alexander Martin",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/men/6.jpg",
|
||||
},
|
||||
{
|
||||
email: "daniel.rodriguez@example.com",
|
||||
fullName: "Daniel Rodriguez",
|
||||
password: "123456",
|
||||
profilePic: "https://randomuser.me/api/portraits/men/7.jpg",
|
||||
},
|
||||
];
|
||||
|
||||
const seedDatabase = async () => {
|
||||
try {
|
||||
await connectDB();
|
||||
|
||||
await User.insertMany(seedUsers);
|
||||
console.log("Database seeded successfully");
|
||||
} catch (error) {
|
||||
console.error("Error seeding database:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Call the function
|
||||
seedDatabase();
|
||||
Reference in New Issue
Block a user