Skip to content

Commit

Permalink
1st part completed - backend
Browse files Browse the repository at this point in the history
  • Loading branch information
burakorkmez committed Jan 29, 2024
0 parents commit 9180cd3
Show file tree
Hide file tree
Showing 28 changed files with 6,296 additions and 0 deletions.
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

.env

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
88 changes: 88 additions & 0 deletions backend/controllers/auth.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import bcrypt from "bcryptjs";
import User from "../models/user.model.js";
import generateTokenAndSetCookie from "../utils/generateToken.js";

export const signup = async (req, res) => {
try {
const { fullName, username, password, confirmPassword, gender } = req.body;

if (password !== confirmPassword) {
return res.status(400).json({ error: "Passwords don't match" });
}

const user = await User.findOne({ username });

if (user) {
return res.status(400).json({ error: "Username already exists" });
}

// HASH PASSWORD HERE
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);

// https://avatar-placeholder.iran.liara.run/

const boyProfilePic = `https://avatar.iran.liara.run/public/boy?username=${username}`;
const girlProfilePic = `https://avatar.iran.liara.run/public/girl?username=${username}`;

const newUser = new User({
fullName,
username,
password: hashedPassword,
gender,
profilePic: gender === "male" ? boyProfilePic : girlProfilePic,
});

if (newUser) {
// Generate JWT token here
generateTokenAndSetCookie(newUser._id, res);
await newUser.save();

res.status(201).json({
_id: newUser._id,
fullName: newUser.fullName,
username: newUser.username,
profilePic: newUser.profilePic,
});
} else {
res.status(400).json({ error: "Invalid user data" });
}
} catch (error) {
console.log("Error in signup controller", error.message);
res.status(500).json({ error: "Internal Server Error" });
}
};

export const login = async (req, res) => {
try {
const { username, password } = req.body;
const user = await User.findOne({ username });
const isPasswordCorrect = await bcrypt.compare(password, user?.password || "");

if (!user || !isPasswordCorrect) {
return res.status(400).json({ error: "Invalid username or password" });
}

generateTokenAndSetCookie(user._id, res);

res.status(200).json({
_id: user._id,
fullName: user.fullName,
username: user.username,
profilePic: user.profilePic,
});
} catch (error) {
console.log("Error in login controller", error.message);
res.status(500).json({ error: "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({ error: "Internal Server Error" });
}
};
63 changes: 63 additions & 0 deletions backend/controllers/message.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import Conversation from "../models/conversation.model.js";
import Message from "../models/message.model.js";

export const sendMessage = async (req, res) => {
try {
const { message } = req.body;
const { id: receiverId } = req.params;
const senderId = req.user._id;

let conversation = await Conversation.findOne({
participants: { $all: [senderId, receiverId] },
});

if (!conversation) {
conversation = await Conversation.create({
participants: [senderId, receiverId],
});
}

const newMessage = new Message({
senderId,
receiverId,
message,
});

if (newMessage) {
conversation.messages.push(newMessage._id);
}

// SOCKET IO FUNCTIONALITY WILL GO HERE

// await conversation.save();
// await newMessage.save();

// this will run in parallel
await Promise.all([conversation.save(), newMessage.save()]);

res.status(201).json(newMessage);
} catch (error) {
console.log("Error in sendMessage controller: ", error.message);
res.status(500).json({ error: "Internal server error" });
}
};

export const getMessages = async (req, res) => {
try {
const { id: userToChatId } = req.params;
const senderId = req.user._id;

const conversation = await Conversation.findOne({
participants: { $all: [senderId, userToChatId] },
}).populate("messages"); // NOT REFERENCE BUT ACTUAL MESSAGES

if (!conversation) return res.status(200).json([]);

const messages = conversation.messages;

res.status(200).json(messages);
} catch (error) {
console.log("Error in getMessages controller: ", error.message);
res.status(500).json({ error: "Internal server error" });
}
};
14 changes: 14 additions & 0 deletions backend/controllers/user.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import User from "../models/user.model.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" });
}
};
12 changes: 12 additions & 0 deletions backend/db/connectToMongoDB.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import mongoose from "mongoose";

const connectToMongoDB = async () => {
try {
await mongoose.connect(process.env.MONGO_DB_URI);
console.log("Connected to MongoDB");
} catch (error) {
console.log("Error connecting to MongoDB", error.message);
}
};

export default connectToMongoDB;
33 changes: 33 additions & 0 deletions backend/middleware/protectRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import jwt from "jsonwebtoken";
import User from "../models/user.model.js";

const protectRoute = async (req, res, next) => {
try {
const token = req.cookies.jwt;

if (!token) {
return res.status(401).json({ error: "Unauthorized - No Token Provided" });
}

const decoded = jwt.verify(token, process.env.JWT_SECRET);

if (!decoded) {
return res.status(401).json({ error: "Unauthorized - Invalid Token" });
}

const user = await User.findById(decoded.userId).select("-password");

if (!user) {
return res.status(404).json({ error: "User not found" });
}

req.user = user;

next();
} catch (error) {
console.log("Error in protectRoute middleware: ", error.message);
res.status(500).json({ error: "Internal server error" });
}
};

export default protectRoute;
24 changes: 24 additions & 0 deletions backend/models/conversation.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import mongoose from "mongoose";

const conversationSchema = new mongoose.Schema(
{
participants: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
messages: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Message",
default: [],
},
],
},
{ timestamps: true }
);

const Conversation = mongoose.model("Conversation", conversationSchema);

export default Conversation;
26 changes: 26 additions & 0 deletions backend/models/message.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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,
},
message: {
type: String,
required: true,
},
// createdAt, updatedAt
},
{ timestamps: true }
);

const Message = mongoose.model("Message", messageSchema);

export default Message;
35 changes: 35 additions & 0 deletions backend/models/user.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import mongoose from "mongoose";

const userSchema = new mongoose.Schema(
{
fullName: {
type: String,
required: true,
},
username: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
minlength: 6,
},
gender: {
type: String,
required: true,
enum: ["male", "female"],
},
profilePic: {
type: String,
default: "",
},
// createdAt, updatedAt => Member since <createdAt>
},
{ timestamps: true }
);

const User = mongoose.model("User", userSchema);

export default User;
12 changes: 12 additions & 0 deletions backend/routes/auth.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import express from "express";
import { login, logout, signup } from "../controllers/auth.controller.js";

const router = express.Router();

router.post("/signup", signup);

router.post("/login", login);

router.post("/logout", logout);

export default router;
10 changes: 10 additions & 0 deletions backend/routes/message.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import express from "express";
import { getMessages, sendMessage } from "../controllers/message.controller.js";
import protectRoute from "../middleware/protectRoute.js";

const router = express.Router();

router.get("/:id", protectRoute, getMessages);
router.post("/send/:id", protectRoute, sendMessage);

export default router;
9 changes: 9 additions & 0 deletions backend/routes/user.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import express from "express";
import protectRoute from "../middleware/protectRoute.js";
import { getUsersForSidebar } from "../controllers/user.controller.js";

const router = express.Router();

router.get("/", protectRoute, getUsersForSidebar);

export default router;
31 changes: 31 additions & 0 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import express from "express";
import dotenv from "dotenv";
import cookieParser from "cookie-parser";

import authRoutes from "./routes/auth.routes.js";
import messageRoutes from "./routes/message.routes.js";
import userRoutes from "./routes/user.routes.js";

import connectToMongoDB from "./db/connectToMongoDB.js";

const app = express();
const PORT = process.env.PORT || 5000;

dotenv.config();

app.use(express.json()); // to parse the incoming requests with JSON payloads (from req.body)
app.use(cookieParser());

app.use("/api/auth", authRoutes);
app.use("/api/messages", messageRoutes);
app.use("/api/users", userRoutes);

// app.get("/", (req, res) => {
// root route http://localhost:5000/
// res.send("Hello World!!");
// });

app.listen(PORT, () => {
connectToMongoDB();
console.log(`Server Running on port ${PORT}`);
});
Loading

0 comments on commit 9180cd3

Please sign in to comment.