Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions backend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
PORT=8000
MONGO_URI=mongodb://127.0.0.1:27017/notesDB
NODE_ENV=development
UPLOAD_DIR=uploads/
23 changes: 23 additions & 0 deletions backend/connector/database.connector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const mongoose = require("mongoose");

const database = async () => {
const MONGO_URI = process.env.MONGO_URI;

if (!MONGO_URI) {
console.error("Missing MONGO_URI in .env");
process.exit(1);
}

try {
await mongoose.connect(MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log("Connected to MongoDB");
} catch (error) {
console.error("MongoDB connection failed:", error.message);
process.exit(1);
}
};

module.exports = database;
30 changes: 30 additions & 0 deletions backend/controllers/notes.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const { successResponse, errorResponse } = require("../utils/response");
const {
handleMultipleFileUpload,
createNoteWithAttachment,
} = require("../services/notes.service");
const { asyncHandler } = require("../utils/asyncHandler");

exports.createNote = asyncHandler(async (req, res) => {
// File + body handled together
const fileMetadataArray = handleMultipleFileUpload(req);
const noteData = {
title: req.body.title,
content: req.body.content,
createdBy: req.body.createdBy,
};

// const savedNote = await createNoteWithAttachment(noteData, fileMetadataArray);
const savedNote = {
...noteData,
attachments: fileMetadataArray,
_id: "mock-id-123",
uploadedAt: new Date(),
}; //remove this(23-28) line when implementing actual DB save
console.log("Saved Note:", savedNote);
return successResponse(
res,
savedNote,
"Note created successfully with attachment"
);
});
37 changes: 32 additions & 5 deletions backend/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
const express = require('express')
const app = express()
const port = 8000
require("dotenv").config();
const path = require("path");
const express = require("express");
const notesRouter = require("./routes/notes.route");
const database = require("./connector/database.connector");
const { errorHandler } = require("./middleware/errorHandler.middleware");
const app = express();
const cors = require("cors");
const port = process.env.PORT ?? 8000;
//CORS setup
app.use(
cors({
origin: "http://localhost:3000", // React app
methods: ["GET", "POST", "PUT", "DELETE"],
credentials: true,
})
);
// Parse JSON bodies
app.use(express.json());

// Serve uploaded files statically (for viewing/downloading)
app.use("/uploads", express.static(path.join(__dirname, "uploads")));

// Notes + file-upload routes
app.use("/api/notes", notesRouter);

// Global error handler
app.use(errorHandler);
//database connection is established in database.connector.js
// database();
// Start server
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
console.log(`Example app listening on port ${port}`);
});
10 changes: 10 additions & 0 deletions backend/middleware/errorHandler.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { errorResponse } = require("../utils/response");

exports.errorHandler = (err, req, res, next) => {
console.error("Global Error:", err);

const statusCode = err.statusCode || 500;
const message = err.message || "Internal Server Error";

return errorResponse(res, message, statusCode, err);
};
27 changes: 27 additions & 0 deletions backend/middleware/notes.validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const { errorResponse } = require("../utils/response");

exports.validateBody = (schema) => (req, res, next) => {
try {
console.log("Incoming body before validation:", req.body);

if (!req.body) {
return res.status(400).json({ message: "Request body is missing" });
}

const data = {
title: req.body.title || "",
content: req.body.content || "",
createdBy: req.body.createdBy || "",
};

const parsed = schema.parse(data);
console.log("Zod Validation Success", parsed);
next();
} catch (error) {
console.error("Zod Validation Error Details:", error);
return res.status(400).json({
message: "Invalid input",
errors: error.errors || [],
});
}
};
40 changes: 40 additions & 0 deletions backend/middleware/upload.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const multer = require("multer");
const path = require("path");

// Storage config – save files in /uploads folder
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.join(__dirname, "..", "uploads"));
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
cb(null, uniqueSuffix + "-" + file.originalname);
},
});

// Allowed file types
const allowedTypes = [
"application/pdf",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"image/jpeg",
"image/png",
];

// Filter invalid file types
function fileFilter(req, file, cb) {
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error("Unsupported file type"), false);
}
}

// 10 MB limit
const upload = multer({
storage,
fileFilter,
limits: { fileSize: 10 * 1024 * 1024 },
});

module.exports = upload;
24 changes: 24 additions & 0 deletions backend/models/notes.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const mongoose = require("mongoose");

const AttachmentSchema = new mongoose.Schema(
{
originalName: { type: String, required: true },
mimeType: { type: String, required: true },
size: { type: Number, required: true },
url: { type: String, required: true },
},
{ _id: false } // prevent extra _id in array
);

const NoteSchema = new mongoose.Schema(
{
title: { type: String, required: true },
content: { type: String, required: true },
createdBy: { type: String, required: true }, // can hold professor’s ID/name
uploadedAt: { type: Date, default: Date.now },
attachments: [AttachmentSchema],
},
{ timestamps: true }
);

module.exports = mongoose.model("Note", NoteSchema);
Loading