Added CommunityPost Image Function
This commit is contained in:
556
client/pnpm-lock.yaml
generated
556
client/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
44
client/src/components/InsertPostImage.tsx
Normal file
44
client/src/components/InsertPostImage.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
interface InsertPostImageProps {
|
||||||
|
onImageSelected: (file: File | null) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InsertPostImage: React.FC<InsertPostImageProps> = ({ onImageSelected }) => {
|
||||||
|
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||||
|
const [previewImage, setPreviewImage] = useState<string>('');
|
||||||
|
|
||||||
|
const handleImageSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const selectedFiles = event.target.files as FileList;
|
||||||
|
const file = selectedFiles?.[0] || null;
|
||||||
|
setSelectedFile(file);
|
||||||
|
setPreviewImage(file ? URL.createObjectURL(file) : '');
|
||||||
|
onImageSelected(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex flex-col dark:bg-zinc-800 rounded-md ${selectedFile ? 'h-auto' : 'h-20'}`}
|
||||||
|
style={{ width: 300 }}>
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
onChange={handleImageSelect}
|
||||||
|
className="mb-3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{selectedFile && (
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
src={previewImage}
|
||||||
|
alt="Selected Image"
|
||||||
|
className="w-full h-full object-cover rounded-md"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InsertPostImage;
|
||||||
@@ -544,17 +544,15 @@ export const TrashDeleteIcon = () => {
|
|||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none" viewBox="0 0 24 24"
|
||||||
viewBox="0 0 24 24"
|
strokeWidth={1.5}
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
className="size-6"
|
className="size-6">
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
stroke-linecap="round"
|
strokeLinecap="round"
|
||||||
stroke-linejoin="round"
|
strokeLinejoin="round"
|
||||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
||||||
/>
|
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export default function CommunityPage() {
|
|||||||
const [search, setSearch] = useState(""); // Search Function
|
const [search, setSearch] = useState(""); // Search Function
|
||||||
const [userInformation, setUserInformation] = useState<Record<string, User>>({});
|
const [userInformation, setUserInformation] = useState<Record<string, User>>({});
|
||||||
const [currentUserInfo, setCurrentUserInfo] = useState(null);
|
const [currentUserInfo, setCurrentUserInfo] = useState(null);
|
||||||
|
const [imageErrorFlags, setImageErrorFlags] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
let accessToken = localStorage.getItem("accessToken");
|
let accessToken = localStorage.getItem("accessToken");
|
||||||
if (!accessToken) {
|
if (!accessToken) {
|
||||||
@@ -80,7 +81,6 @@ export default function CommunityPage() {
|
|||||||
setCommunityList(res.data);
|
setCommunityList(res.data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getPosts();
|
getPosts();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -165,6 +165,7 @@ export default function CommunityPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handlePostClick = (id: string) => {
|
const handlePostClick = (id: string) => {
|
||||||
navigate(`post/${id}`);
|
navigate(`post/${id}`);
|
||||||
};
|
};
|
||||||
@@ -174,6 +175,13 @@ export default function CommunityPage() {
|
|||||||
return `${config.serverAddress}/users/profile-image/${userId}`;
|
return `${config.serverAddress}/users/profile-image/${userId}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleImageError = (postId: string) => {
|
||||||
|
setImageErrorFlags((prevFlags) => ({
|
||||||
|
...prevFlags,
|
||||||
|
[postId]: true,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<div className="flex flex-row gap-4 m-10">
|
<div className="flex flex-row gap-4 m-10">
|
||||||
@@ -244,9 +252,13 @@ export default function CommunityPage() {
|
|||||||
<div>
|
<div>
|
||||||
<p>{post.content}</p>
|
<p>{post.content}</p>
|
||||||
</div>
|
</div>
|
||||||
|
{!imageErrorFlags[post.id] && post.postImage && post.postImage !== null && (
|
||||||
<div>
|
<div>
|
||||||
<p>Image</p>
|
<img src={`${config.serverAddress}/post/post-image/${post.id}`}
|
||||||
|
className="w-[300px] h-auto rounded-lg object-cover"
|
||||||
|
onError={() => handleImageError(post.id)} />
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import config from "../config";
|
|||||||
import { ArrowUTurnLeftIcon } from "../icons";
|
import { ArrowUTurnLeftIcon } from "../icons";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { retrieveUserInformation } from "../security/users";
|
import { retrieveUserInformation } from "../security/users";
|
||||||
|
import InsertPostImage from "../components/InsertPostImage";
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
title: Yup.string()
|
title: Yup.string()
|
||||||
@@ -29,16 +30,20 @@ const validationSchema = Yup.object({
|
|||||||
"Only letters, numbers, commas, spaces, exclamation marks, quotations, and common symbols are allowed"
|
"Only letters, numbers, commas, spaces, exclamation marks, quotations, and common symbols are allowed"
|
||||||
)
|
)
|
||||||
.required("Content is required"),
|
.required("Content is required"),
|
||||||
|
postImage: Yup.mixed(),
|
||||||
});
|
});
|
||||||
|
|
||||||
function CreatePostPage() {
|
function CreatePostPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [userId, setUserId] = useState(null);
|
const [userId, setUserId] = useState(null);
|
||||||
|
// Add state for image selection
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
title: "",
|
title: "",
|
||||||
content: "",
|
content: "",
|
||||||
|
postImage: null,
|
||||||
tags: "",
|
tags: "",
|
||||||
|
userId: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -55,17 +60,30 @@ function CreatePostPage() {
|
|||||||
|
|
||||||
const handleSubmit = async (
|
const handleSubmit = async (
|
||||||
values: any,
|
values: any,
|
||||||
{ setSubmitting, resetForm, setFieldError }: any
|
{ setSubmitting, resetForm, setFieldError, setFieldValue }: any
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const postData = {
|
const formData = new FormData();
|
||||||
...values,
|
formData.append("title", values.title);
|
||||||
userId: userId
|
formData.append("content", values.content);
|
||||||
|
if (values.postImage) {
|
||||||
|
formData.append("postImage", values.postImage);
|
||||||
}
|
}
|
||||||
const response = await axios.post(config.serverAddress + "/post", postData); // Assuming an API route
|
formData.append("tags", values.tags);
|
||||||
|
formData.append("userId", userId || ""); // Ensure userId is appended to formData
|
||||||
|
|
||||||
|
console.log("Submitting formData:", formData);
|
||||||
|
|
||||||
|
const response = await axios.post(config.serverAddress + "/post", formData, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
console.log("Post created successfully:", response.data);
|
console.log("Post created successfully:", response.data);
|
||||||
resetForm(); // Clear form after successful submit
|
resetForm(); // Clear form after successful submit
|
||||||
|
setFieldValue("postImage", null);
|
||||||
navigate(-1);
|
navigate(-1);
|
||||||
} else {
|
} else {
|
||||||
console.error("Error creating post:", response.statusText);
|
console.error("Error creating post:", response.statusText);
|
||||||
@@ -99,7 +117,7 @@ function CreatePostPage() {
|
|||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
{({ isValid, dirty, isSubmitting }) => (
|
{({ isValid, dirty, isSubmitting, setFieldValue }) => (
|
||||||
<Form className="flex flex-col gap-5">
|
<Form className="flex flex-col gap-5">
|
||||||
<div>
|
<div>
|
||||||
<NextUIFormikInput
|
<NextUIFormikInput
|
||||||
@@ -110,11 +128,6 @@ function CreatePostPage() {
|
|||||||
labelPlacement="inside"
|
labelPlacement="inside"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm">
|
|
||||||
<div>
|
|
||||||
<p>Image</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<NextUIFormikTextarea
|
<NextUIFormikTextarea
|
||||||
label="Content"
|
label="Content"
|
||||||
@@ -131,10 +144,19 @@ function CreatePostPage() {
|
|||||||
labelPlacement="inside"
|
labelPlacement="inside"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-sm">
|
||||||
|
<div className="flex flex-row gap-10">
|
||||||
|
<InsertPostImage
|
||||||
|
onImageSelected={(file) => {
|
||||||
|
setFieldValue("postImage", file);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="flex flex-row-reverse">
|
<div className="flex flex-row-reverse">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="bg-primary-color text-white text-xl w-1/12"
|
className="bg-primary-950 text-white text-xl w-1/12"
|
||||||
disabled={!isValid || !dirty || isSubmitting}
|
disabled={!isValid || !dirty || isSubmitting}
|
||||||
>
|
>
|
||||||
<p>Post</p>
|
<p>Post</p>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import NextUIFormikTextarea from "../components/NextUIFormikTextarea";
|
|||||||
import config from "../config";
|
import config from "../config";
|
||||||
import instance from "../security/http";
|
import instance from "../security/http";
|
||||||
import { ArrowUTurnLeftIcon } from "../icons";
|
import { ArrowUTurnLeftIcon } from "../icons";
|
||||||
|
import InsertPostImage from "../components/InsertPostImage";
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
title: Yup.string()
|
title: Yup.string()
|
||||||
@@ -29,20 +30,26 @@ const validationSchema = Yup.object({
|
|||||||
"Only letters, numbers, commas, spaces, exclamation marks, quotations, and common symbols are allowed"
|
"Only letters, numbers, commas, spaces, exclamation marks, quotations, and common symbols are allowed"
|
||||||
)
|
)
|
||||||
.required("Content is required"),
|
.required("Content is required"),
|
||||||
|
postImage: Yup.mixed(),
|
||||||
});
|
});
|
||||||
|
|
||||||
function editPost() {
|
function EditPostPage() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [post, setPost] = useState({
|
const [post, setPost] = useState({
|
||||||
title: "",
|
title: "",
|
||||||
content: "",
|
content: "",
|
||||||
|
postImage: null,
|
||||||
|
tags: "",
|
||||||
});
|
});
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
instance.get(config.serverAddress + `/post/${id}`).then((res) => {
|
instance.get(config.serverAddress + `/post/${id}`).then((res) => {
|
||||||
setPost(res.data);
|
setPost({
|
||||||
|
...res.data,
|
||||||
|
postImage: `${config.serverAddress}/post/post-image/${id}`, // Set image URL
|
||||||
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}, [id]);
|
}, [id]);
|
||||||
@@ -52,13 +59,24 @@ function editPost() {
|
|||||||
{ setSubmitting, resetForm, setFieldError }: any
|
{ setSubmitting, resetForm, setFieldError }: any
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("title", values.title);
|
||||||
|
formData.append("content", values.content);
|
||||||
|
if (values.postImage) {
|
||||||
|
formData.append("postImage", values.postImage);
|
||||||
|
}
|
||||||
|
formData.append("tags", values.tags);
|
||||||
|
|
||||||
const response = await instance.put(
|
const response = await instance.put(
|
||||||
config.serverAddress + `/post/${id}`,
|
config.serverAddress + `/post/${id}`,
|
||||||
values
|
formData,
|
||||||
|
{ headers: { "Content-Type": "multipart/form-data" } }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
console.log("Post updated successfully:", response.data);
|
console.log("Post updated successfully:", response.data);
|
||||||
resetForm();
|
resetForm();
|
||||||
|
// Set a flag to indicate a refresh is needed
|
||||||
navigate(-1);
|
navigate(-1);
|
||||||
} else {
|
} else {
|
||||||
console.error("Error updating post:", response.statusText);
|
console.error("Error updating post:", response.statusText);
|
||||||
@@ -93,7 +111,7 @@ function editPost() {
|
|||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
{({ isValid, dirty, isSubmitting }) => (
|
{({ isValid, dirty, isSubmitting, setFieldValue }) => (
|
||||||
<Form className="flex flex-col gap-5">
|
<Form className="flex flex-col gap-5">
|
||||||
<div>
|
<div>
|
||||||
<NextUIFormikInput
|
<NextUIFormikInput
|
||||||
@@ -104,9 +122,6 @@ function editPost() {
|
|||||||
labelPlacement="inside"
|
labelPlacement="inside"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm">
|
|
||||||
<p>Image</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<NextUIFormikTextarea
|
<NextUIFormikTextarea
|
||||||
label="Content"
|
label="Content"
|
||||||
@@ -123,10 +138,19 @@ function editPost() {
|
|||||||
labelPlacement="inside"
|
labelPlacement="inside"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-sm">
|
||||||
|
<div className="flex flex-row gap-10">
|
||||||
|
<InsertPostImage
|
||||||
|
onImageSelected={(file) => {
|
||||||
|
setFieldValue("postImage", file);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="flex flex-row-reverse">
|
<div className="flex flex-row-reverse">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="bg-primary-color text-white text-xl w-1/6"
|
className="bg-primary-950 text-white text-xl w-1/6"
|
||||||
disabled={!isValid || !dirty || isSubmitting}
|
disabled={!isValid || !dirty || isSubmitting}
|
||||||
>
|
>
|
||||||
<p>Update</p>
|
<p>Update</p>
|
||||||
@@ -141,4 +165,4 @@ function editPost() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default editPost;
|
export default EditPostPage;
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ const PostPage: React.FC = () => {
|
|||||||
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
||||||
const [selectedPost, setSelectedPost] = useState<Post | null>(null);
|
const [selectedPost, setSelectedPost] = useState<Post | null>(null);
|
||||||
const [userInformation, setUserInformation] = useState<Record<string, User>>({});
|
const [userInformation, setUserInformation] = useState<Record<string, User>>({});
|
||||||
|
const [imageErrorFlags, setImageErrorFlags] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id) {
|
if (id) {
|
||||||
@@ -57,7 +58,7 @@ const PostPage: React.FC = () => {
|
|||||||
.then((res) => setPost(res.data))
|
.then((res) => setPost(res.data))
|
||||||
.catch((error) => console.error("Error fetching post:", error));
|
.catch((error) => console.error("Error fetching post:", error));
|
||||||
}
|
}
|
||||||
}, [id]);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (post) {
|
if (post) {
|
||||||
@@ -112,6 +113,13 @@ const PostPage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
const profilePictureUrl = post ? getProfilePicture(post.userId) : "";
|
const profilePictureUrl = post ? getProfilePicture(post.userId) : "";
|
||||||
|
|
||||||
|
const handleImageError = (postId: string) => {
|
||||||
|
setImageErrorFlags((prevFlags) => ({
|
||||||
|
...prevFlags,
|
||||||
|
[postId]: true,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<section className="flex">
|
<section className="flex">
|
||||||
@@ -179,15 +187,14 @@ const PostPage: React.FC = () => {
|
|||||||
<div>
|
<div>
|
||||||
<p>{post.content}</p>
|
<p>{post.content}</p>
|
||||||
</div>
|
</div>
|
||||||
|
{!imageErrorFlags[post.id] && post.postImage && post.postImage !== null && (
|
||||||
<div>
|
<div>
|
||||||
<p>Image</p>
|
<img src={`${config.serverAddress}/post/post-image/${post.id}`}
|
||||||
{/* {userInformation && (
|
alt="PostImage"
|
||||||
<UserPostImage
|
className="w-[300px] h-auto rounded-lg object-cover"
|
||||||
userId={userInformation}
|
onError={() => handleImageError(post.id)} />
|
||||||
editable={true}
|
|
||||||
/>
|
|
||||||
)} */}
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"mysql2": "^3.10.1",
|
"mysql2": "^3.10.1",
|
||||||
"nodemon": "^3.1.3",
|
"nodemon": "^3.1.3",
|
||||||
"openai": "^4.53.2",
|
"openai": "^4.53.2",
|
||||||
|
"react-router-dom": "^6.26.0",
|
||||||
"sequelize": "^6.37.3",
|
"sequelize": "^6.37.3",
|
||||||
"sharp": "^0.33.4",
|
"sharp": "^0.33.4",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
|
|||||||
2002
server/pnpm-lock.yaml
generated
2002
server/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -16,18 +16,21 @@ filter.addWords(...newBadWords);
|
|||||||
let removeWords = [''];
|
let removeWords = [''];
|
||||||
filter.removeWords(...removeWords);
|
filter.removeWords(...removeWords);
|
||||||
|
|
||||||
router.post("/", async (req, res) => {
|
const upload = multer({ storage: multer.memoryStorage() });
|
||||||
|
|
||||||
|
router.post("/", upload.fields([{ name: 'postImage', maxCount: 1 }]), async (req, res) => {
|
||||||
let data = req.body;
|
let data = req.body;
|
||||||
|
let files = req.files;
|
||||||
|
|
||||||
// Validate request body
|
// Validate request body
|
||||||
let validationSchema = yup.object({
|
let validationSchema = yup.object({
|
||||||
title: yup.string().trim().min(3).max(200).required(), // yup object to define validation schema
|
title: yup.string().trim().min(3).max(200).required(),
|
||||||
content: yup.string().trim().min(3).max(500).required(),
|
content: yup.string().trim().min(3).max(500).required(),
|
||||||
|
userId: yup.string().required(),
|
||||||
postImage: yup.string().trim().max(255),
|
postImage: yup.string().trim().max(255),
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
data = await validationSchema.validate(data, // validate() method is used to validate data against the schema and returns the valid data and any applied transformations
|
data = await validationSchema.validate(data, { abortEarly: false });
|
||||||
{ abortEarly: false }); // abortEarly: false means the validation won’t stop when the first error is detected
|
|
||||||
// Process valid data
|
|
||||||
|
|
||||||
// Check for profanity
|
// Check for profanity
|
||||||
if (filter.isProfane(data.title)) {
|
if (filter.isProfane(data.title)) {
|
||||||
@@ -37,14 +40,18 @@ router.post("/", async (req, res) => {
|
|||||||
return res.status(400).json({ field: 'content', error: 'Profane content detected in content' });
|
return res.status(400).json({ field: 'content', error: 'Profane content detected in content' });
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await Post.create(data); // sequelize method create() is used to insert data into the database table
|
let postImage = files.postImage ? files.postImage[0].buffer : null;
|
||||||
|
|
||||||
|
// Process valid data
|
||||||
|
let result = await Post.create({ ...data, postImage });
|
||||||
res.json(result);
|
res.json(result);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
res.status(400).json({ errors: err.errors }); // If the error is caught, return the bad request
|
res.status(400).json({ errors: err.errors });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// // sequelize method findAll is used to generate a standard SELECT query which will retrieve all entries from the table
|
// // sequelize method findAll is used to generate a standard SELECT query which will retrieve all entries from the table
|
||||||
// router.get("/", async (req, res) => {
|
// router.get("/", async (req, res) => {
|
||||||
// let list = await Tutorial.findAll({
|
// let list = await Tutorial.findAll({
|
||||||
@@ -85,19 +92,44 @@ router.get("/:id", async (req, res) => {
|
|||||||
res.json(post);
|
res.json(post);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put("/:id", async (req, res) => {
|
router.get("/post-image/:id", async (req, res) => {
|
||||||
let id = req.params.id;
|
let id = req.params.id;
|
||||||
|
let post = await Post.findByPk(id);
|
||||||
|
|
||||||
|
if (!post || !post.postImage) {
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
res.set("Content-Type", "image/jpeg"); // Adjust the content type as necessary
|
||||||
|
res.send(post.postImage);
|
||||||
|
} catch (err) {
|
||||||
|
res
|
||||||
|
.status(500)
|
||||||
|
.json({ message: "Error retrieving post image", error: err });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put("/:id", upload.fields([{ name: 'postImage', maxCount: 1 }]), async (req, res) => {
|
||||||
|
let id = req.params.id;
|
||||||
|
let files = req.files;
|
||||||
|
|
||||||
// Check id not found
|
// Check id not found
|
||||||
let post = await Post.findByPk(id);
|
let post = await Post.findByPk(id);
|
||||||
if (!post) {
|
if (!post) {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = req.body;
|
let data = req.body;
|
||||||
|
let postImage = files.postImage ? files.postImage[0].buffer : null;
|
||||||
|
|
||||||
// Validate request body
|
// Validate request body
|
||||||
let validationSchema = yup.object({
|
let validationSchema = yup.object({
|
||||||
title: yup.string().trim().min(3).max(100),
|
title: yup.string().trim().min(3).max(100),
|
||||||
content: yup.string().trim().min(3).max(500)
|
content: yup.string().trim().min(3).max(500),
|
||||||
|
postImage: yup.mixed(),
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
data = await validationSchema.validate(data,
|
data = await validationSchema.validate(data,
|
||||||
@@ -111,14 +143,17 @@ router.put("/:id", async (req, res) => {
|
|||||||
return res.status(400).json({ field: 'content', error: 'Profane content detected in content' });
|
return res.status(400).json({ field: 'content', error: 'Profane content detected in content' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include the postImage if present
|
||||||
|
if (postImage) {
|
||||||
|
data.postImage = postImage;
|
||||||
|
}
|
||||||
|
|
||||||
// Process valid data
|
// Process valid data
|
||||||
let post = await Post.update(data, { // update() updates data based on the where condition, and returns the number of rows affected
|
let post = await Post.update(data, { // update() updates data based on the where condition, and returns the number of rows affected
|
||||||
where: { id: id } // If num equals 1, return OK, otherwise return Bad Request
|
where: { id: id } // If num equals 1, return OK, otherwise return Bad Request
|
||||||
});
|
});
|
||||||
if (post == 1) {
|
if (post) {
|
||||||
res.json({
|
res.json({ message: "Post was updated successfully." });
|
||||||
message: "Post was updated successfully."
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
|
|||||||
Reference in New Issue
Block a user