diff --git a/backend/package-lock.json b/backend/package-lock.json index fe3ad00..3967c09 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -22,7 +22,8 @@ "jsonwebtoken": "^9.0.3", "multer": "^2.1.1", "postmark": "^4.0.7", - "prisma": "^6.19.2" + "prisma": "^6.19.2", + "slugify": "^1.6.9" }, "devDependencies": { "nodemon": "^3.1.11" @@ -1727,7 +1728,6 @@ "integrity": "sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/config": "6.19.2", "@prisma/engines": "6.19.2" @@ -2064,6 +2064,15 @@ "node": ">=10" } }, + "node_modules/slugify": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.9.tgz", + "integrity": "sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", diff --git a/backend/package.json b/backend/package.json index 4f05cfd..b18dc4c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,7 +28,8 @@ "jsonwebtoken": "^9.0.3", "multer": "^2.1.1", "postmark": "^4.0.7", - "prisma": "^6.19.2" + "prisma": "^6.19.2", + "slugify": "^1.6.9" }, "devDependencies": { "nodemon": "^3.1.11" diff --git a/backend/prisma/migrations/20260414083619_added_slug/migration.sql b/backend/prisma/migrations/20260414083619_added_slug/migration.sql new file mode 100644 index 0000000..1b64c8f --- /dev/null +++ b/backend/prisma/migrations/20260414083619_added_slug/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `slug` to the `Blog` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Blog" ADD COLUMN "slug" TEXT NOT NULL; diff --git a/backend/prisma/migrations/20260414091055/migration.sql b/backend/prisma/migrations/20260414091055/migration.sql new file mode 100644 index 0000000..aaf6685 --- /dev/null +++ b/backend/prisma/migrations/20260414091055/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[slug]` on the table `Blog` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "Blog_slug_key" ON "Blog"("slug"); diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index f78df85..9cc2df9 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -93,6 +93,7 @@ model Blog { image String? content Json isActive Boolean @default(true) + slug String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -149,20 +150,20 @@ model Appointment { } model Inquiry { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) - fullName String - number String - emailId String? - subject String? - message String? + fullName String + number String + emailId String? + subject String? + message String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model AcademicsResearch { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) fullName String number String @@ -171,24 +172,23 @@ model AcademicsResearch { courseName String? message String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } - model EmailConfig { - id Int @id @default(autoincrement()) - name String - email String - type String - isActive Boolean @default(true) + id Int @id @default(autoincrement()) + name String + email String + type String + isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model NewsMedia { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) headline String content String? @@ -197,8 +197,8 @@ model NewsMedia { author String? date DateTime? - isActive Boolean @default(true) + isActive Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt -} \ No newline at end of file + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} diff --git a/backend/src/controllers/blog.controller.js b/backend/src/controllers/blog.controller.js index bf58a09..c0a839a 100644 --- a/backend/src/controllers/blog.controller.js +++ b/backend/src/controllers/blog.controller.js @@ -1,9 +1,10 @@ import prisma from "../prisma/client.js"; +import slugify from "slugify"; /* CREATE BLOG */ export async function createBlog(req, res) { - const {title, writer, image, content, isActive} = req.body; + const { title, writer, image, content, isActive } = req.body; try { const blog = await prisma.blog.create({ @@ -13,12 +14,13 @@ export async function createBlog(req, res) { image, content, isActive, + slug: slugify(title), }, }); res.json(blog); } catch (error) { - res.status(500).json({error: "Blog creation failed"}); + res.status(500).json({ error: "Blog creation failed" }); } } @@ -27,13 +29,13 @@ export async function createBlog(req, res) { export async function getBlogs(req, res) { try { const blogs = await prisma.blog.findMany({ - where: {isActive: true}, - orderBy: {createdAt: "desc"}, + where: { isActive: true }, + orderBy: { createdAt: "desc" }, }); res.json(blogs); } catch (error) { - res.status(500).json({error: error.message}); + res.status(500).json({ error: error.message }); } } @@ -42,12 +44,12 @@ export async function getBlogs(req, res) { export async function getAllBlogs(req, res) { try { const blogs = await prisma.blog.findMany({ - orderBy: {createdAt: "desc"}, + orderBy: { createdAt: "desc" }, }); res.json(blogs); } catch (error) { - res.status(500).json({error: error.message}); + res.status(500).json({ error: error.message }); } } @@ -55,19 +57,40 @@ export async function getAllBlogs(req, res) { export async function getBlog(req, res) { try { - const id = Number(req.params.id); + const slug = req.params.slug; + console.log({ slug }); const blog = await prisma.blog.findUnique({ - where: {id}, + where: { slug }, }); if (!blog) { - return res.status(404).json({error: "Blog not found"}); + return res.status(404).json({ error: "Blog not found" }); } res.json(blog); } catch (error) { - res.status(500).json({error: error.message}); + res.status(500).json({ error: error.message }); + } +} + +/* GET SINGLE BLOG (ADMIN)*/ + +export async function getBlogForAdmin(req, res) { + try { + const id = Number(req.params.id); + + const blog = await prisma.blog.findUnique({ + where: { id }, + }); + + if (!blog) { + return res.status(404).json({ error: "Blog not found" }); + } + + res.json(blog); + } catch (error) { + res.status(500).json({ error: error.message }); } } @@ -75,10 +98,10 @@ export async function getBlog(req, res) { export async function updateBlog(req, res) { try { - const {title, writer, image, content} = req.body; + const { title, writer, image, content } = req.body; const blog = await prisma.blog.update({ - where: {id: Number(req.params.id)}, + where: { id: Number(req.params.id) }, data: { title, writer, @@ -89,7 +112,7 @@ export async function updateBlog(req, res) { res.json(blog); } catch (error) { - res.status(500).json({error: error.message}); + res.status(500).json({ error: error.message }); } } @@ -100,11 +123,11 @@ export async function deleteBlog(req, res) { const id = Number(req.params.id); await prisma.blog.delete({ - where: {id}, + where: { id }, }); - res.json({message: "Blog deleted successfully"}); + res.json({ message: "Blog deleted successfully" }); } catch (error) { - res.status(500).json({error: error.message}); + res.status(500).json({ error: error.message }); } } diff --git a/backend/src/routes/blog.routes.js b/backend/src/routes/blog.routes.js index 7cf9ad2..26a825b 100644 --- a/backend/src/routes/blog.routes.js +++ b/backend/src/routes/blog.routes.js @@ -6,6 +6,7 @@ import { updateBlog, deleteBlog, getAllBlogs, + getBlogForAdmin, } from "../controllers/blog.controller.js"; import jwtAuthMiddleware from "../middleware/auth.js"; @@ -15,11 +16,14 @@ const router = express.Router(); /* PUBLIC */ router.get("/", getBlogs); -router.get("/:id", getBlog); +router.get("/:slug", getBlog); // Protected router.get("/admin/all", jwtAuthMiddleware, getAllBlogs); + +router.get("/admin/:id", jwtAuthMiddleware, getBlogForAdmin); + router.post("/", jwtAuthMiddleware, createBlog); router.put("/:id", jwtAuthMiddleware, updateBlog); router.delete("/:id", jwtAuthMiddleware, deleteBlog); diff --git a/backend/uploads/blog/1776156111743.png b/backend/uploads/blog/1776156111743.png new file mode 100644 index 0000000..0781df1 Binary files /dev/null and b/backend/uploads/blog/1776156111743.png differ