Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b1a2582
Added Database Schemas and Diagrams
CoderFleet Feb 1, 2025
f974b99
fix/mistyped Schema name
CoderFleet Feb 1, 2025
50eb230
Basic Util Files ⚡ and Password Hashing
CoderFleet Feb 1, 2025
f5c2e69
Merge pull request #2 from CoderFleet/rudransh
CoderFleet Feb 1, 2025
e17299a
feat: Password Verification, JWT Token Gen, implement multer Multer C…
CoderFleet Feb 1, 2025
91e872c
feat: Add cookie parser and static file serving; implement Cloudinary…
CoderFleet Feb 1, 2025
7a20544
fix: added cloudinary credentials to .env
CoderFleet Feb 1, 2025
736dabb
refactor: file deletion
CoderFleet Feb 1, 2025
b7343ad
feat: Implement user registration and login routes with avatar upload
CoderFleet Feb 1, 2025
ffd1f6f
feat: Implement user login functionality with access and refresh toke…
CoderFleet Feb 1, 2025
b8a4144
feat: Add logout functionality and JWT verification middleware
CoderFleet Feb 2, 2025
b7c2f85
feat: Add token refresh and current user retrieval functionality
CoderFleet Feb 2, 2025
e65239f
fix: Fixed Cloudinary upload bug
CoderFleet Feb 2, 2025
0e508c7
feat: create Assignment with file upload support
CoderFleet Feb 2, 2025
2fd5c75
feat: get userAssignments and protected routes
CoderFleet Feb 2, 2025
979ca01
feat: add update and delete functionality for assignments
CoderFleet Feb 2, 2025
59905ec
Merge pull request #6 from CoderFleet/rudransh
CoderFleet Feb 2, 2025
a49243f
Merge pull request #7 from CoderFleet/shreeya
shreesriv12 Feb 2, 2025
e5fad04
fix: landing page
Venu4i Feb 2, 2025
13dd1af
fix:signup page
Venu4i Feb 2, 2025
dc22f4c
feat: implement Tasks controllers
CoderFleet Feb 2, 2025
0659468
feat: add task routes and integrate with main app
CoderFleet Feb 2, 2025
a81293b
Merge pull request #12 from CoderFleet/rudransh
CoderFleet Feb 3, 2025
0aecd4e
feat: Messaging functionality setup
CoderFleet Feb 3, 2025
055daf2
feat: implement messaging functionality with getMessages and sendMess…
CoderFleet Feb 3, 2025
4885ba6
feat: setup
CoderFleet Feb 4, 2025
69f88ff
feat: add socket.io integration and update various components for cha…
CoderFleet Feb 7, 2025
4df812b
feat: update route protection to check for authenticated user
CoderFleet Feb 7, 2025
f727b7a
.
CoderFleet Feb 8, 2025
069780d
feat: enhance UI with new font and layout adjustments; update task an…
CoderFleet Feb 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.9.5",
"mongoose-aggregate-paginate-v2": "^1.1.3",
"multer": "^1.4.5-lts.1"
"multer": "^1.4.5-lts.1",
"socket.io": "^4.8.1"
},
"devDependencies": {
"nodemon": "^3.1.9",
Expand Down
23 changes: 22 additions & 1 deletion backend/src/app.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
import express from "express";
import cors from "cors";
import cookieParser from "cookie-parser";

const app = express();

app.use(
cors({
origin: process.env.CORS_ORIGIN,
origin: "http://localhost:5173",
credentials: true,
})
);

app.use(express.json({ limit: "16kb" }));
app.use(express.urlencoded({ extended: true, limit: "16kb" }));

// Configuring express to mark public as static storage folder
app.use(express.static("public"));

// Cookie Parser configuration for tokens
app.use(cookieParser());

// TODO: Routes will go here ✔
import userRouter from "./routes/user.routes.js";
import assignmentRouter from "./routes/assignment.routes.js";
import taskRouter from "./routes/task.routes.js";
import messageRouter from "./routes/message.routes.js";
import scheduleRouter from "./routes/schedule.routes.js";

// Routes Declaration
app.use("/users", userRouter);
app.use("/assignments", assignmentRouter);
app.use("/tasks", taskRouter);
app.use("/messages", messageRouter);
app.use("/schedules", scheduleRouter);

export { app };
151 changes: 151 additions & 0 deletions backend/src/controllers/assignment.controllers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { Assignment } from "../models/assignment.models.js";
import { ApiError } from "../utils/ApiError.js";
import { ApiResponse } from "../utils/ApiResponse.js";
import { asyncHandler } from "../utils/asyncHandler.js";
import { uploadToCloud, cloud } from "../utils/cloudinary.js";
import mongoose, { isValidObjectId } from "mongoose";

const createAssignment = asyncHandler(async (req, res) => {
const { title, description, due_date } = req.body;

if ([title, due_date].some((field) => field?.trim() === "")) {
throw new ApiError(400, "Some fields are required fields");
}

const docs = req.files
? req.files.map(async (file) => {
const localPath = file.path;
console.log(localPath);
const doc = await uploadToCloud(localPath);

return doc?.url;
})
: [];

const uploadedDocs = await Promise.all(docs);

const assignment = await Assignment.create({
title,
description,
docs: uploadedDocs,
due_date,
user: req.user._id,
});

const assignmentFromDB = await Assignment.findById(assignment._id);

if (!assignmentFromDB) {
throw new ApiError(500, "Something went wrong while creating Assignment");
}

return res
.status(201)
.json(new ApiResponse(200, assignmentFromDB, "Assignment Ban Gaya 🤩!"));
});

const getUserAssignments = asyncHandler(async (req, res) => {
const userId = req.user.id;

const assignments = await Assignment.find({ user: userId }).sort({
createdAt: -1,
});

// if (!assignments.length) {
// throw new ApiError(404, "No assignments found for this user...");
// }

return res
.status(200)
.json(new ApiResponse(200, assignments, "Assignments Fetched"));
});

const updateAssignment = asyncHandler(async (req, res) => {
const { assignmentId } = req.params;
const { title, description, due_date } = req.body;

// Validate ObjectId
if (!isValidObjectId(assignmentId)) {
throw new ApiError(400, "Invalid Assignment ID");
}

// const assignment = await Assignment.findById(assignmentId);
// if (!assignment) {
// throw new ApiError(404, "Assignment not found");
// }
// if (assignment.user.toString() !== req.user._id.toString()) {
// throw new ApiError(403, "Unauthorized to update this assignment");
// }

// Better approach (apparently😭)
const assignment = await Assignment.findOne({
_id: assignmentId,
user: req.user._id,
});
// Validation Again 💀
if (!assignment) {
throw new ApiError(404, "Assignment not found or unauthorized access");
}

let uploadedDocs = assignment.docs;
if (req.files && req.files.length > 0) {
const docs = req.files.map(async (file) => {
const localPath = file.path;
return await uploadToCloud(localPath).url;
});
const newDocs = await Promise.all(docs);
uploadedDocs = [...uploadedDocs, ...newDocs]; // Can't afford to loose previously uploaded docs
}

assignment.docs = uploadedDocs;

assignment.title = title || assignment.title;
assignment.description = description || assignment.description;
assignment.due_date = due_date || assignment.due_date;

await assignment.save();

return res
.status(200)
.json(new ApiResponse(200, assignment, "Assignment Updates Successfully"));
});

const deleteAssignment = asyncHandler(async (req, res) => {
const { assignmentId } = req.params;

if (!mongoose.Types.ObjectId.isValid(assignmentId)) {
throw new ApiError(400, "Invalid Assignment ID.");
}

const assignment = await Assignment.findOne({
_id: assignmentId,
user: req.user._id,
});
// Validation Again 💀
if (!assignment) {
throw new ApiError(404, "Assignment not found or unauthorized access");
}

// Remove files from Cloudinary
if (assignment.docs.length > 0) {
const deleteFilePromises = assignment.docs.map(async (fileUrl) => {
try {
const publicId = fileUrl.split("/").pop().split(".")[0]; // Extract public ID (got this from stackoverflow)
await cloud.uploader.destroy(publicId);
} catch (error) {
console.error(`Failed to delete file: ${fileUrl}`, error);
}
});
await Promise.all(deleteFilePromises);
}

await Assignment.findByIdAndDelete(assignmentId);

return res.status(200).json(new ApiResponse(200, {}, "Khatam tata bye bye"));
});

export {
createAssignment,
getUserAssignments,
updateAssignment,
deleteAssignment,
};
99 changes: 99 additions & 0 deletions backend/src/controllers/message.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { User } from "../models/user.models.js";
import Message from "../models/message.models.js";
import { ApiError } from "../utils/ApiError.js";
import { ApiResponse } from "../utils/ApiResponse.js";
import { asyncHandler } from "../utils/asyncHandler.js";
import { uploadToCloud } from "../utils/cloudinary.js";
import { getReceiverSocketId, io } from "../utils/socket.js";

const getUsers = asyncHandler(async (req, res) => {
try {
const loggedUserId = req.user._id;

// Get all users except the logged in user
const filteredUsers = await User.find({
_id: { $ne: loggedUserId },
}).select("-password");

if (!filteredUsers) {
return res.status(404).json(new ApiResponse(404, null, "No users found"));
}

return res
.status(200)
.json(new ApiResponse(200, filteredUsers, "Users fetched"));
} catch (error) {
console.error("Error in getUsersForSidebar: ", error.message);
throw new ApiError(500, "Something went wrong while fetching users");
}
});

const getMessages = asyncHandler(async (req, res) => {
try {
const loggedUserId = req.user._id;
const receiverId = req.params.id;

// Fetching messages
const messages = await Message.find({
$or: [
{ senderId: loggedUserId, receiverId: receiverId },
{ senderId: receiverId, receiverId: loggedUserId },
],
}).sort({ createdAt: 1 });

if (!messages) {
return res
.status(404)
.json(new ApiResponse(404, null, "No messages found"));
}

return res
.status(200)
.json(new ApiResponse(200, messages, "Messages fetched"));
} catch (error) {
console.error("Error in getMessages: ", error.message);
throw new ApiError(500, "Something went wrong while fetching messages");
}
});

const sendMessage = asyncHandler(async (req, res) => {
try {
const loggedUserId = req.user._id;
const receiverId = req.params.id;
const { text } = req.body;

let doc = null;
const file = req.file;
if (file) {
const localPath = file.path;
doc = await uploadToCloud(localPath);

if (!doc) {
throw new ApiError(400, "File couldn't be saved");
}
}

const message = await Message.create({
senderId: loggedUserId,
receiverId: receiverId,
text,
doc: doc ? doc.url : null,
});

if (!message) {
throw new ApiError(400, "Message couldn't be saved");
}

const receiverSocketId = getReceiverSocketId(receiverId);
if (receiverSocketId) {
io.to(receiverSocketId).emit("newMessage", message);
}

return res.status(201).json(new ApiResponse(201, message, "Message sent"));
} catch (error) {
console.error("Error in sendMessage: ", error.message);
throw new ApiError(500, "Something went wrong while sending message");
}
});

export { getUsers, getMessages, sendMessage };
74 changes: 74 additions & 0 deletions backend/src/controllers/schedule.controllers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Schedule } from "../models/schedules.models.js";
import { ApiError } from "../utils/ApiError.js";
import { ApiResponse } from "../utils/ApiResponse.js";
import { asyncHandler } from "../utils/asyncHandler.js";
import mongoose from "mongoose";

// const createSchedule = asyncHandler(async (req, res) => {
// const { reminderDate, assignmentId, taskId, message } = req.body;

// if (!reminderDate) {
// throw new ApiError(400, "date is required");
// }

// if (assignmentId && taskId) {
// throw new ApiError(
// 400,
// "Schedule can be linked to either an assignment or a task, not both."
// );
// }

// const schedule = await Schedule.create({
// user: req.user._id,
// reminderDate,
// assignmentId: assignmentId || null,
// taskId: taskId || null,
// message: message || "Karle Bhai Complete",
// });

// return res
// .status(201)
// .json(new ApiResponse(201, schedule, "Schedule Bangaya 🤩!"));
// });

const getUserSchedules = asyncHandler(async (req, res) => {
const userId = req.user._id;

const schedules = await Schedule.find({ user: userId })
.populate("assignmentId", "title due_date")
.populate("taskId", "title deadline")
.sort({ reminderDate: 1 });

if (!schedules.length) {
throw new ApiError(404, "No Schedule");
}

return res
.status(200)
.json(new ApiResponse(200, schedules, "Schedules Fetched Successfully"));
});

const deleteSchedule = asyncHandler(async (req, res) => {
const { scheduleId } = req.params;

if (!mongoose.Types.ObjectId.isValid(scheduleId)) {
throw new ApiError(400, "Invalid ScheduleId.");
}

const schedule = await Schedule.findOne({
_id: scheduleId,
user: req.user._id,
});

if (!schedule) {
throw new ApiError(404, "Schedule not found or unauthorized access");
}

await Schedule.findByIdAndDelete(scheduleId);

return res
.status(200)
.json(new ApiResponse(200, {}, "Schedule Deleted Successfully"));
});

export { getUserSchedules, deleteSchedule };
Loading