Skip to content
Closed
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
3 changes: 3 additions & 0 deletions backend/firebase-service-account.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"get it from your firebase console to use store image feature"
}
3,854 changes: 3,263 additions & 591 deletions backend/package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@
"author": "",
"license": "MIT",
"dependencies": {
"@aws-sdk/client-s3": "^3.921.0",
"bcryptjs": "^3.0.2",
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.6.1",
"express": "^4.21.2",
"firebase-admin": "^13.5.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.19.2",
"morgan": "^1.10.0",
"multer": "^2.0.2",
"nodemailer": "^7.0.9",
"nodemon": "^3.1.10",
"uuid": "^13.0.0"
Expand Down
3 changes: 2 additions & 1 deletion backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import bcrypt from "bcryptjs";
import cookieParser from "cookie-parser";
import { signAccessToken, signRefreshToken, verifyRefreshToken } from "./controllers/auth";
import { authenticate, AuthRequest } from "./middleware/authMiddleware";
import dotenv from "dotenv";

const app: Application = express();
import dotenv from "dotenv";

dotenv.config();

// Middleware setup
Expand Down
62 changes: 14 additions & 48 deletions backend/src/controllers/product.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,37 @@ import Product from "../models/product.model";

export const createProduct = async (req: Request, res: Response) => {
try {
const { name, description, price, category, stock } = req.body;
const { name, description, price, category, stock, imageUrl, condition } = req.body;

if (!name || !description || !price) {
return res.status(400).json({ message: "Missing required fields" });
if (!name || !description || !price || !category || !stock) {
return res.status(400).json({ error: "Missing required field(s)" });
}

const product = await Product.create({
const product = new Product({
name,
description,
price,
category,
stock,
imageUrl,
condition,
});
return res.status(201).json(product);
} catch (error) {
console.error("Create Product Error:", error);
res.status(500).json({ message: "Server error" });

await product.save();
res.status(201).json(product);
} catch (err: any) {
res.status(500).json({ error: err.message || "Server error" });
}
};

export const getProducts = async (req: Request, res: Response) => {
try {
const { search, category, minPrice, maxPrice, sort } = req.query;

const query: any = {};

// 🔍 Search by name or description
if (search) {
query.$or = [
{ name: { $regex: search, $options: "i" } },
{ description: { $regex: search, $options: "i" } },
];
}

// 🏷️ Filter by category
if (category && category !== "all") {
query.category = category;
}

// 💰 Filter by price range
if (minPrice || maxPrice) {
query.price = {};
if (minPrice) query.price.$gte = Number(minPrice);
if (maxPrice) query.price.$lte = Number(maxPrice);
}

// 🧾 Build base query
let mongoQuery = Product.find(query);

// 📊 Sorting
if (sort === "asc" || sort === "lowToHigh")
mongoQuery = mongoQuery.sort({ price: 1 });
else if (sort === "desc" || sort === "highToLow")
mongoQuery = mongoQuery.sort({ price: -1 });
else mongoQuery = mongoQuery.sort({ createdAt: -1 });

const products = await mongoQuery;

const products = await Product.find().sort({ createdAt: -1 });
res.status(200).json(products);
} catch (error) {
console.error("Get Products Error:", error);
res.status(500).json({ message: "Server error" });
} catch (err: any) {
res.status(500).json({ error: err.message });
}
};

export const getProductById = async (req: Request, res: Response) => {
try {
const product = await Product.findById(req.params.id);
Expand Down
19 changes: 19 additions & 0 deletions backend/src/middleware/uploadMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import multer from "multer";

const upload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 2 * 1024 * 1024 },
fileFilter: (_req: any, file: { mimetype: string; }, cb: (arg0: Error | null, arg1: boolean | undefined) => void) => {
if (
file.mimetype === "image/jpeg" ||
file.mimetype === "image/png" ||
file.mimetype === "image/webp"
) {
cb(null, true);
} else {
cb(new Error("Invalid file type. Only JPEG, PNG, WEBP allowed."));
}
},
});

export default upload;
4 changes: 3 additions & 1 deletion backend/src/models/product.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface IProduct extends Document {
price: number;
category?: string;
stock: number;
imageUrl?: string;
createdAt: Date;
updatedAt: Date;
}
Expand All @@ -17,8 +18,9 @@ const productSchema = new Schema<IProduct>(
price: { type: Number, required: true },
category: { type: String },
stock: { type: Number, default: 0 },
imageUrl: { type: String },
},
{ timestamps: true }
);

export default mongoose.model<IProduct>("Product", productSchema);
export default mongoose.model<IProduct>("Product", productSchema);
14 changes: 8 additions & 6 deletions backend/src/routes/cartRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import {
getCart,
checkout,
} from "../controllers/cart.controller";
import { authenticate } from "../middleware/authMiddleware";

const router: Router = express.Router();

router.post("/", addItem);
router.patch("/:itemId", updateQuantity);
router.delete("/:itemId", removeItem);
router.get("/", getCart);
router.post("/checkout", checkout);
//Protected routes
router.post("/", authenticate, addItem);
router.patch("/:itemId", authenticate, updateQuantity);
router.delete("/:itemId", authenticate, removeItem);
router.get("/", authenticate, getCart);
router.post("/checkout", authenticate, checkout);

export default router;
export default router;
1 change: 1 addition & 0 deletions frontend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_API_BASE_URL=http://localhost:5000
1 change: 1 addition & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_API_BASE_URL=http://localhost:5000
Loading
Loading