Initial Commit

This commit is contained in:
bansh_dml 2025-01-24 10:27:47 +05:30
parent 838e619efc
commit 0c35fee8e3
41 changed files with 1748 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
node_modules
# Keep environment variables out of version control
.env

4
blogs.csv Normal file
View file

@ -0,0 +1,4 @@
slug,title,body,categoryIDs,tagIDs
slug1,Bansh Vatsa,Blog Post,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"
slug2,Sunny Jha,Traveller,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"
slug3,Rohit Sharma,Cricketer,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"
Can't render this file because it contains an unexpected character in line 2 and column 31.

1207
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

23
package.json Normal file
View file

@ -0,0 +1,23 @@
{
"name": "blog-backend",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@prisma/client": "^6.2.1",
"body-parser": "^1.20.3",
"csv-parser": "^3.1.0",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express-validator": "^7.2.1",
"joi": "^17.13.3",
"multer": "^1.4.5-lts.1",
"prisma": "^6.2.1"
}
}

46
prisma/schema.prisma Normal file
View file

@ -0,0 +1,46 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL") //In the env file
}
model Blog {
id Int @id @default(autoincrement())
slug String
title String
body String
coverImage String?
categoryIDs String // or Int[] depending on your use case
tagIDs String // or Int[] depending on your use case
createdAt DateTime @default(now())
}
model Category {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
blogIDs String[] @db.ObjectId
blogs Blog[] @relation("BlogCategories", fields: [blogIDs], references: [id])
}
model Tag {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
blogIDs String[] @db.ObjectId
blogs Blog[] @relation("BlogTags", fields: [blogIDs], references: [id])
}
model Comment {
id String @id @default(auto()) @map("_id") @db.ObjectId
comment String
blogId String @db.ObjectId
blog Blog @relation(fields: [blogId], references: [id])
}

18
src/app.js Normal file
View file

@ -0,0 +1,18 @@
const express = require("express");
const blogRoutes = require("./routes/blogRoutes");
const categoryRoutes = require("./routes/categoryRoutes");
const tagRoutes = require("./routes/tagRoutes");
const commentRoutes = require("./routes/commentRoutes");
const app = express();
// Middleware
app.use(express.json());
app.use("/uploads", express.static("uploads"));
// Routes
app.use("/api/blogs", blogRoutes);
app.use("/api/categories", categoryRoutes);
app.use("/api/tags", tagRoutes);
app.use("/api/comments", commentRoutes);
module.exports = app;

View file

@ -0,0 +1,133 @@
const { PrismaClient } = require("@prisma/client");
const fs = require("fs");
const csv = require("csv-parser");
const prisma = new PrismaClient();
const blogSchema = require("../validations");
// Create a blog
exports.createBlog = async (req, res) => {
try {
const { slug, title, body, categoryIDs, tagIDs } = req.body;
const coverImage = req.file ? req.file.path : null;
const newBlog = await prisma.blog.create({
data: {
slug,
title,
body,
coverImage,
// Ensure categoryIDs and tagIDs are correctly processed
categoryIDs: categoryIDs ? categoryIDs.split(",") : [],
tagIDs: tagIDs ? tagIDs.split(",") : [],
},
});
res.status(201).json(newBlog);
} catch (error) {
console.error("Error creating blog:", error);
res.status(500).json({ error: error.message });
}
};
exports.bulkUploadBlogs = async (req, res) => {
try {
const results = [];
await new Promise((resolve, reject) => {
fs.createReadStream(req.file.path)
.pipe(csv())
.on("data", (data) => results.push(data))
.on("end", resolve)
.on("error", reject);
});
const blogs = await prisma.blog.createMany({
data: results.map((blog) => ({
slug: blog.slug,
title: blog.title,
body: blog.body,
categoryIDs: blog.categoryIDs ? JSON.parse(blog.categoryIDs) : [],
tagIDs: blog.tagIDs ? JSON.parse(blog.tagIDs) : [],
})),
});
res.status(201).json({ count: blogs.count });
} catch (error) {
console.error("Error in bulk upload:", error);
res.status(500).json({ error: error.message });
}
};
// Get all blogs
exports.getBlogs = async (req, res) => {
try {
const blogs = await prisma.blog.findMany({
include: { categories: true, tags: true, comments: true },
});
res.status(200).json(blogs);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Update a blog
exports.updateBlog = async (req, res) => {
try {
const { id } = req.params;
const { slug, title, body, categoryIDs, tagIDs } = req.body;
// Update the blog
const updatedBlog = await prisma.blog.update({
where: { id },
data: {
slug,
title,
body,
categoryIDs: categoryIDs.map((id) => new prisma.ObjectId(id)), // Ensure IDs are converted
tagIDs: tagIDs.map((id) => new prisma.ObjectId(id)), // Ensure IDs are converted
},
});
res.status(200).json(updatedBlog);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Delete a blog
exports.deleteBlog = async (req, res) => {
try {
const { id } = req.params;
await prisma.blog.delete({ where: { id } });
res.status(204).send();
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// const { PrismaClient } = require("@prisma/client");
// const prisma = new PrismaClient();
// const fs = require('fs');
// const path = require('path');
// // Create a blog with a cover image
// exports.createBlog = async (req, res) => {
// try {
// const { slug, title, body, categoryIDs, tagIDs } = req.body;
// const coverImage = req.file ? req.file.path : null;
// // Create the blog post
// const newBlog = await prisma.blog.create({
// data: {
// slug,
// title,
// body,
// coverImage,
// categoryIDs,
// tagIDs,
// },
// });
// res.status(201).json(newBlog);
// } catch (error) {
// console.error("Error creating blog:", error);
// res.status(500).json({ error: "Internal server error" });
// }
// };

View file

@ -0,0 +1,51 @@
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
// Create a category
exports.createCategory = async (req, res) => {
try {
const { name } = req.body;
const newCategory = await prisma.category.create({
data: { name },
});
res.status(201).json(newCategory);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Get all categories
exports.getCategories = async (req, res) => {
try {
const categories = await prisma.category.findMany();
res.status(200).json(categories);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Update a category
exports.updateCategory = async (req, res) => {
try {
const { id } = req.params;
const { name } = req.body;
const updatedCategory = await prisma.category.update({
where: { id },
data: { name },
});
res.status(200).json(updatedCategory);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Delete a category
exports.deleteCategory = async (req, res) => {
try {
const { id } = req.params;
await prisma.category.delete({ where: { id } });
res.status(204).send();
} catch (error) {
res.status(500).json({ error: error.message });
}
};

View file

@ -0,0 +1,56 @@
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
// Create a comment
exports.createComment = async (req, res) => {
try {
const { comment, blogId } = req.body;
const newComment = await prisma.comment.create({
data: {
comment,
blogId,
},
});
res.status(201).json(newComment);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Get all comments
exports.getComments = async (req, res) => {
try {
const comments = await prisma.comment.findMany();
res.status(200).json(comments);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Update a comment
exports.updateComment = async (req, res) => {
try {
const { id } = req.params;
const { comment } = req.body;
const updatedComment = await prisma.comment.update({
where: { id },
data: { comment },
});
res.status(200).json(updatedComment);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Delete a comment
exports.deleteComment = async (req, res) => {
try {
const { id } = req.params;
await prisma.comment.delete({
where: { id },
});
res.status(204).send();
} catch (error) {
res.status(500).json({ error: error.message });
}
};

View file

@ -0,0 +1,53 @@
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
// Create a tag
exports.createTag = async (req, res) => {
try {
const { name } = req.body;
const newTag = await prisma.tag.create({
data: { name },
});
res.status(201).json(newTag);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Get all tags
exports.getTags = async (req, res) => {
try {
const tags = await prisma.tag.findMany();
res.status(200).json(tags);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Update a tag
exports.updateTag = async (req, res) => {
try {
const { id } = req.params;
const { name } = req.body;
const updatedTag = await prisma.tag.update({
where: { id },
data: { name },
});
res.status(200).json(updatedTag);
} catch (error) {
res.status(500).json({ error: error.message });
}
};
// Delete a tag
exports.deleteTag = async (req, res) => {
try {
const { id } = req.params;
await prisma.tag.delete({
where: { id },
});
res.status(204).send();
} catch (error) {
res.status(500).json({ error: error.message });
}
};

18
src/routes/blogRoutes.js Normal file
View file

@ -0,0 +1,18 @@
const express = require("express");
const multer = require('multer');
const { createBlog, getBlogs, updateBlog, deleteBlog, bulkUploadBlogs } = require("../controllers/blogController");
const { validateBlog } = require("../validations");
const router = express.Router();
const upload = multer({ dest: 'uploads/' });
// Ensure each route has a properly defined controller function
router.post("/", upload.single('coverImage'), validateBlog, createBlog);
router.post("/bulk-upload", upload.single('csvFile'), bulkUploadBlogs);
router.get("/", getBlogs);
router.put("/:id", updateBlog);
router.delete("/:id", deleteBlog);
module.exports = router;

View file

@ -0,0 +1,15 @@
const express = require("express");
const {
createCategory,
getCategories,
updateCategory,
deleteCategory,
} = require("../controllers/categoryController");
const router = express.Router();
router.post("/", createCategory); // Create a new category
router.get("/", getCategories); // Get all categories
router.put("/:id", updateCategory); // Update a category by ID
router.delete("/:id", deleteCategory); // Delete a category by ID
module.exports = router;

View file

@ -0,0 +1,16 @@
const express = require("express");
const {
createComment,
getComments,
updateComment,
deleteComment,
} = require("../controllers/commentController");
const router = express.Router();
router.post("/", createComment); // Create a comment
router.get("/", getComments); // Get all comments
router.put("/:id", updateComment); // Update a comment
router.delete("/:id", deleteComment); // Delete a comment
module.exports = router;

16
src/routes/tagRoutes.js Normal file
View file

@ -0,0 +1,16 @@
const express = require("express");
const {
createTag,
getTags,
updateTag,
deleteTag,
} = require("../controllers/tagController"); // Make sure the path is correct
const router = express.Router();
router.post("/", createTag); // Create a tag
router.get("/", getTags); // Get all tags
router.put("/:id", updateTag); // Update a tag
router.delete("/:id", deleteTag); // Delete a tag
module.exports = router;

14
src/server.js Normal file
View file

@ -0,0 +1,14 @@
const app = require("./app.js");
const express =require("express");
const dotenv = require("dotenv");
const { PrismaClient } = require("@prisma/client");
dotenv.config();
const prisma = new PrismaClient();
app.use(express.json());
const PORT = 5000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

Binary file not shown.

View file

@ -0,0 +1,4 @@
slug,title,body,categoryIDs,tagIDs
slug1,Bansh Vatsa,Blog Post,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"
slug2,Sunny Jha,Traveller,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"
slug3,Rohit Sharma,Cricketer,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Bansh Vatsa,Blog Post,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"
slug2,Salman Khan,Actor,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"[\"67907d341beea453148d6f13\"]","[\"67907d341beea453148d6f13\"]"
slug2,Title 2,Body 2,"[\"67907d341beea453148d6f13\"]","[\"67907d341beea453148d6f13\"]"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"
slug2,Title 2,Body 2,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

Binary file not shown.

View file

@ -0,0 +1,2 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
first-blog,First Blog,Content 1,category1,tag1
second-blog,Second Blog,Content 2,category2,tag2

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"[\"67907d341beea453148d6f13\"]","[\"67907d341beea453148d6f13\"]"
slug2,Title 2,Body 2,"[\"67907d341beea453148d6f13\"]","[\"67907d341beea453148d6f13\"]"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

View file

@ -0,0 +1,3 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"67907d341beea453148d6f13","67907d341beea453148d6f13"
slug2,Title 2,Body 2,"67907d341beea453148d6f13","67907d341beea453148d6f13"

View file

@ -0,0 +1,4 @@
slug,title,body,categoryIDs,tagIDs
slug1,Title 1,Body 1,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"
slug2,Title 2,Body 2,"["67907d341beea453148d6f13"]","["67907d341beea453148d6f13"]"
slug3,Bansh,Mountains,"["travell"],"["123"]"

Binary file not shown.

20
src/validations.js Normal file
View file

@ -0,0 +1,20 @@
const Joi = require('joi');
const blogSchema = Joi.object({
slug: Joi.string().required(),
title: Joi.string().required(),
body: Joi.string().required(),
categoryIDs: Joi.array().items(Joi.string()),
tagIDs: Joi.array().items(Joi.string()),
coverImage: Joi.string()
});
const validateBlog = (req, res, next) => {
const { error } = blogSchema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
next();
};
module.exports = { validateBlog };