diff --git a/backend/src/controllers/blog.controller.js b/backend/src/controllers/blog.controller.js index cf0c2040..7f42515a 100644 --- a/backend/src/controllers/blog.controller.js +++ b/backend/src/controllers/blog.controller.js @@ -53,6 +53,15 @@ export const createBlog = asyncHandler(async (req, res) => { throw new ApiError(400, "Title, excerpt, content, and category are required"); } + // Validate minimum lengths + if (title.trim().length < 5) { + throw new ApiError(400, "Title must be at least 5 characters long"); + } + + if (content.trim().length < 20) { + throw new ApiError(400, "Content must be at least 20 characters long"); + } + if (!req.file) { throw new ApiError(400, "Blog image is required"); } @@ -158,9 +167,27 @@ export const updateBlogById = asyncHandler(async (req, res) => { } const updateData = {}; - if (title) updateData.title = title.trim(); + if (title !== undefined && title !== null) { + const trimmedTitle = title.trim(); + if (!trimmedTitle) { + throw new ApiError(400, "Title cannot be empty"); + } + if (trimmedTitle.length < 5) { + throw new ApiError(400, "Title must be at least 5 characters long"); + } + updateData.title = trimmedTitle; + } if (excerpt) updateData.excerpt = excerpt.trim(); - if (content) updateData.content = content.trim(); + if (content !== undefined && content !== null) { + const trimmedContent = content.trim(); + if (!trimmedContent) { + throw new ApiError(400, "Content cannot be empty"); + } + if (trimmedContent.length < 20) { + throw new ApiError(400, "Content must be at least 20 characters long"); + } + updateData.content = trimmedContent; + } if (category) updateData.category = category.trim(); if (tags) updateData.tags = parseTags(tags).map((tag) => tag.trim()); diff --git a/src/Pages/AddBlogPage.jsx b/src/Pages/AddBlogPage.jsx index 379b290b..4f8ecffa 100644 --- a/src/Pages/AddBlogPage.jsx +++ b/src/Pages/AddBlogPage.jsx @@ -43,6 +43,32 @@ export default function AddBlogPage() { category: "", }); + const [errors, setErrors] = useState({ + title: "", + content: "", + }); + + const validateForm = () => { + const newErrors = { + title: "", + content: "", + }; + + const trimmedTitle = newBlog.title.trim(); + const trimmedContent = newBlog.content.trim(); + + if (trimmedTitle.length < 5) { + newErrors.title = "Title must be at least 5 characters long"; + } + + if (trimmedContent.length < 20) { + newErrors.content = "Content must be at least 20 characters long"; + } + + setErrors(newErrors); + return !newErrors.title && !newErrors.content; + }; + const handleImageChange = (e) => { const file = e.target.files[0]; if (!file) return; @@ -72,6 +98,11 @@ export default function AddBlogPage() { return; } + // Validate form before submission + if (!validateForm()) { + return; + } + const title = newBlog.title.trim(); const excerpt = newBlog.excerpt.trim(); const content = newBlog.content.trim(); @@ -156,10 +187,14 @@ export default function AddBlogPage() { placeholder="Enter your blog title..." className="w-full px-3 py-2 rounded-xl border-2 border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-800 focus:border-blue-500 focus:ring-4 focus:ring-blue-500/20 transition-all" value={newBlog.title} - onChange={(e) => - setNewBlog({ ...newBlog, title: e.target.value }) - } + onChange={(e) => { + setNewBlog({ ...newBlog, title: e.target.value }); + if (errors.title) setErrors((prev) => ({ ...prev, title: "" })); + }} /> + {errors.title && ( +
{errors.title}
+ )}{errors.content}
+ )}