diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index 0f42389..0000000
Binary files a/.DS_Store and /dev/null differ
diff --git a/.gitignore b/.gitignore
index e033760..d28864a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
# customs ignore
+.DS_Store
# Logs
logs
diff --git a/client/src/lib/index.js b/client/src/lib/index.js
deleted file mode 100644
index 856f2b6..0000000
--- a/client/src/lib/index.js
+++ /dev/null
@@ -1 +0,0 @@
-// place files you want to import through the `$lib` alias in this folder.
diff --git a/client/src/routes/(app)/+layout.svelte b/client/src/routes/(app)/+layout.svelte
index 5da07dd..d93db1a 100644
--- a/client/src/routes/(app)/+layout.svelte
+++ b/client/src/routes/(app)/+layout.svelte
@@ -1,11 +1,12 @@
-
+
+
+
+
+
+
+
+
+
-
-
{$user.firstname} {$user.lastname}
+
+ {#if loading}
+
Loading...
+ {:else}
+
{$user.first_name} {$user.last_name}
-
- {#if $page.url.pathname != "/home"}
-
-
-
- {/if}
-
- {#if $page.url.pathname != "/settings"}
-
-
-
- {/if}
-
-
-
+ {/if}
+
+
-
+
\ No newline at end of file
+
diff --git a/client/src/routes/(app)/home/+page.svelte b/client/src/routes/(app)/home/+page.svelte
index 4e64034..6b0ec76 100644
--- a/client/src/routes/(app)/home/+page.svelte
+++ b/client/src/routes/(app)/home/+page.svelte
@@ -5,6 +5,7 @@
import { goto } from "$app/navigation";
let backendUrl;
+ let loading = true;
onMount(async () => {
backendUrl = `http://${window.location.hostname}:3001/`;
@@ -12,24 +13,18 @@
});
async function fetchPosts() {
+ loading = true;
try {
- const response = await fetch(`${backendUrl}getPosts`);
+ const response = await fetch(`${backendUrl}getPosts`, {
+ method: "GET",
+ credentials: "include",
+ });
if (!response.ok) {
throw new Error("Network response was not ok " + response.statusText);
}
const data = await response.json();
-
- data.posts.forEach((post) => {
- post.comments = [];
- post.likes = 0;
- });
- posts.set(
- data.posts.map((post) => ({
- ...post,
- comments: [],
- likes: 0,
- }))
- );
+ posts.set(data.posts);
+ loading = false;
} catch (error) {
console.error(
"There has been a problem with your fetch operation:",
@@ -38,43 +33,80 @@
}
}
- let openedCommentsPostId = 0;
+ let openedCommentsId = 0;
let newComment = "";
- function addComment(postId) {
- const postsData = get(posts);
- const postIndex = postsData.findIndex((post) => post.id === postId);
- if (postIndex !== -1) {
- const updatedPost = { ...postsData[postIndex] };
- updatedPost.comments.push({
- commenter: `${get(user).firstname} ${get(user).lastname}`,
- comment: newComment,
+ async function addComment(post_id) {
+ try {
+ const response = await fetch(`${backendUrl}addComment`, {
+ method: "POST",
+ credentials: "include",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ post_id: post_id, content: newComment }),
+ });
+
+ if (!response.ok) {
+ throw new Error("Network response was not ok " + response.statusText);
+ }
+ const data = await response.json();
+
+ posts.update((postsArray) => {
+ const updatedPosts = postsArray.map((post) => {
+ if (post.post_id === post_id) {
+ post.comments.push({
+ comment_id: data.comment_id,
+ user_id: get(user).user_id,
+ content: newComment,
+ created_at: new Date().toISOString(),
+ first_name: get(user).first_name,
+ last_name: get(user).last_name,
+ });
+ }
+ return post;
+ });
+ return updatedPosts;
});
- postsData[postIndex] = updatedPost;
- posts.set(postsData);
newComment = "";
+ } catch (error) {
+ console.error("There has been a problem with your comment", error);
}
}
- function addLike(postId) {
- fetch(`${backendUrl}likePost`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ postId }),
- })
- .then(() => {
- const postsData = get(posts);
- const postIndex = postsData.findIndex((post) => post.id === postId);
- if (postIndex !== -1) {
- postsData[postIndex].likes += 1;
- posts.set(postsData);
- }
- })
- .catch((err) => {
- console.log("Error", err);
+ async function addLike(post_id) {
+ try {
+ const response = await fetch(`${backendUrl}likePost`, {
+ method: "POST",
+ credentials: "include",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ post_id: post_id }),
+ });
+
+ if (!response.ok) {
+ throw new Error("Network response was not ok " + response.statusText);
+ }
+ const data = await response.json();
+ console.log(data);
+
+ posts.update((postsArray) => {
+ const updatedPosts = postsArray.map((post) => {
+ if (post.post_id === post_id) {
+ if(data.message === "Like removed successfully")
+ post.like_count = Number(post.like_count) - 1;
+ else if(data.message === "Like created successfully"){
+ post.like_count = Number(post.like_count) + 1;
+ }
+ }
+ return post;
+ });
+ return updatedPosts;
});
+ } catch (error) {
+ console.error("There has been a problem with your like", error);
+ }
}
@@ -82,61 +114,87 @@
-
-
- {#each $posts as post (post.id)}
- -
-
-
-
-
{post.title}
-
by {post.firstname} {post.lastname}
+ {#if loading}
+
+
Loading...
+
+ {:else}
+
+ {#each $posts as post (post.post_id)}
+ -
+
+
+
+
{post.title}
+ by {post.first_name} {post.last_name}
+
-
-
{@html post.content}
-
-
- {#if openedCommentsPostId === post.id}
-
- {#each post.comments as comment}
- -
- {comment.commenter}: {@html comment.comment}
-
- {/each}
-
-
+
{@html post.content}
+
- {/if}
-
- {/each}
-
+ {#if openedCommentsId === post.post_id}
+
+ {#each post.comments as comment}
+ -
+ {comment.first_name}
+ {comment.last_name}: {@html comment.content}
+
+ {/each}
+
+
+
+ {/if}
+
+ {/each}
+
+ {/if}
+
+
diff --git a/client/src/routes/(app)/settings/+page.svelte b/client/src/routes/(app)/settings/+page.svelte
index 5c909b4..cc6cf2a 100644
--- a/client/src/routes/(app)/settings/+page.svelte
+++ b/client/src/routes/(app)/settings/+page.svelte
@@ -4,6 +4,12 @@
import { goto } from "$app/navigation";
let backendUrl;
+ let showAlert = false;
+ let validationError = "";
+ let userForm;
+ let file;
+
+ $: userForm = $user;
onMount(() => {
backendUrl = `http://${window.location.hostname}:3001/`;
@@ -29,52 +35,14 @@
}
}
- let showAlert = false;
- let validationError = "";
-
- let userForm = $user;
- let file;
-
- function updateUser(newData) {
- user.update((u) => {
- return { ...u, ...newData };
- });
- }
-
function handleFileChange(event) {
file = event.target.files[0];
}
- async function saveChanges() {
+ async function handleSaveChanges() {
try {
- // First, send JSON data
- const response = await fetch(`${backendUrl}updateUser`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(userForm),
- });
-
- if (!response.ok) {
- throw new Error("Failed to save changes");
- }
-
- // Then, send the image as FormData if it exists
- if (file) {
- const formData = new FormData();
- formData.append("avatar", file, file.name);
-
- const imgResponse = await fetch(`${backendUrl}updateUserAvatar`, {
- method: "POST",
- body: formData,
- });
-
- if (!imgResponse.ok) {
- throw new Error("Failed to upload image");
- }
- }
-
+ await saveUserData();
+ if (file) await uploadAvatar();
console.log("Changes and image upload successful:", userForm);
updateUser(userForm);
} catch (err) {
@@ -83,6 +51,37 @@
validationError = err.message || "Something went wrong";
}
}
+
+ async function saveUserData() {
+ const response = await fetch(`${backendUrl}updateUser`, {
+ method: "POST",
+ credentials: "include",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(userForm)
+ });
+ if (!response.ok) throw new Error("Failed to save changes");
+ }
+
+ async function uploadAvatar() {
+ const formData = new FormData();
+ formData.append("avatar", file, file.name);
+ const imgResponse = await fetch(`${backendUrl}updateUserAvatar`, {
+ method: "POST",
+ credentials: "include",
+ body: formData
+ });
+ if (!imgResponse.ok) throw new Error("Failed to upload image");
+ }
+
+ async function deleteUser() {
+ const response = await fetch(`${backendUrl}user`, {
+ method: "DELETE",
+ credentials: "include",
+ });
+ if (!response.ok) throw new Error("Failed to delete user");
+
+ goto("/");
+ }
@@ -90,7 +89,7 @@
@@ -134,6 +133,7 @@
@@ -152,6 +152,20 @@
/>
+ {#if showAlert}
+
+
+ Something went wrong
+
+
+ {validationError}
+
+
+ {/if}
+
Logout
-
- {#if showAlert}
-
-
- Something went wrong
-
-
- {validationError}
-
- {/if}
+
+
+
+ Danger Zone
+
\ No newline at end of file
diff --git a/client/src/routes/(app)/write/+page.svelte b/client/src/routes/(app)/write/+page.svelte
index a37d541..fa6b5d5 100644
--- a/client/src/routes/(app)/write/+page.svelte
+++ b/client/src/routes/(app)/write/+page.svelte
@@ -1,13 +1,66 @@
+ function Upload() {
+ try {
+ fetch(`${backendUrl}write`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({title:Post.Title, content:Post.Content})
+ });
+ } catch(err) {
+ console.error(err);
+ }
+
+ Reset();
+ }
+
+ function Reset(){
+ Post.Title = "";
+ Post.Content = "";
+ }
+
-
\ No newline at end of file
+
+
diff --git a/client/src/routes/login/+page.svelte b/client/src/routes/login/+page.svelte
index c62e244..4d73651 100644
--- a/client/src/routes/login/+page.svelte
+++ b/client/src/routes/login/+page.svelte
@@ -3,8 +3,6 @@
import { user } from "../../store/store.js";
import { onMount } from "svelte";
- let email = "";
- let password = "";
let showAlert = false;
let validationError = "";
let backendUrl;
@@ -23,7 +21,7 @@
headers: {
"Content-Type": "application/json",
},
- body: JSON.stringify({ email, password }),
+ body: JSON.stringify({ email: $user.email, password: $user.password }),
});
if (!response.ok) {
validationError = "Invalid login credentials";
@@ -42,10 +40,10 @@
function validateFields() {
validationError = "";
- if (!email || !password) {
+ if (!$user.email || !$user.password) {
validationError = "All fields must be filled out.";
showAlert = true;
- } else if (!/\S+@\S+\.\S+/.test(email)) {
+ } else if (!/\S+@\S+\.\S+/.test($user.email)) {
validationError = "Invalid email format.";
showAlert = true;
}else{
@@ -85,7 +83,7 @@
>Email
@@ -98,7 +96,7 @@
>Password
@@ -200,6 +200,7 @@
>
@@ -214,6 +215,7 @@
diff --git a/client/src/store/store.js b/client/src/store/store.js
index 271ab93..06f158e 100644
--- a/client/src/store/store.js
+++ b/client/src/store/store.js
@@ -1,14 +1,35 @@
import { writable } from 'svelte/store';
export let user = writable({
- id: null,
- firstname: "",
- lastname: "",
+ user_id: null,
+ first_name: "",
+ last_name: "",
email: "",
password: "",
- avatar_path: "",
+ avatar: "",
});
export let posts = writable([
-
- ]);
\ No newline at end of file
+ {
+ "id": 0,
+ "user_id": 0,
+ "post_id": 0,
+ "first_name": "",
+ "last_name": "",
+ "avatar": "",
+ "title": "",
+ "content": "",
+ "timestamp": "",
+ "comments": [
+ {
+ "comment_id": 0,
+ "user_id": 0,
+ "content": "",
+ "created_at": "",
+ "first_name": "",
+ "last_name": "",
+ },
+ ],
+ "like_count": 0,
+ }
+]);
\ No newline at end of file
diff --git a/src/app.js b/src/app.js
index 87fd3c0..b3e47d4 100644
--- a/src/app.js
+++ b/src/app.js
@@ -4,11 +4,12 @@ const path = require("path");
var cookieParser = require("cookie-parser");
const multer = require("multer");
const { connectDatabase } = require("./database/connectionconfigDb");
+const api = require("./api");
+const { notFound, errorHandler } = require("./middlewares/errors.middleware");
const app = express();
-const api = require("./api");
-const { notFound, errorHandler } = require("./middlewares/errors.middleware");
+// ----------------- MIDDLEWARES ----------------- //
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/public"));
@@ -19,10 +20,10 @@ if (process.env.NODE_ENV === "production") {
const storage = multer.diskStorage({
destination: function (req, file, cb) {
- cb(null, "src/uploads/"); // Destination folder
+ cb(null, "src/uploads/");
},
filename: function (req, file, cb) {
- cb(null, Date.now() + "-" + file.originalname); // Naming file
+ cb(null, Date.now() + "-" + file.originalname);
},
});
@@ -35,44 +36,39 @@ app.use(express.json());
const corsOptions = {
credentials: true,
origin: (origin, callback) => {
- const allowedOrigin = `${origin}`;
- callback(null, allowedOrigin);
- }
+ const allowedOrigin = `${origin}`;
+ callback(null, allowedOrigin);
+ },
};
app.use(cors(corsOptions));
+// ----------------- USERS ----------------- //
+
app.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
res.status(400).json({ message: "Invalid Request" });
- // stop the execution if the username or password is missing
return;
}
- // connect to the database
- database = await connectDatabase();
- // Verify the user and password are correct from the post request
- // verify the user exists in the request
- database
- .query(
- `SELECT id,email,firstname,lastname,avatar_path FROM users WHERE email='${email}' AND password='${password}';`
- )
- .then((result) => {
- // If the user is found, return the user information
- if (result.rows.length > 0) {
- // save the user in the session
- res.cookie("user", JSON.stringify(result.rows), {
- maxAge: 3600000 * 24,
- httpOnly: false, // The cookie is accessible via JavaScript
- secure: false, // The cookie will be transmitted over HTTP
- });
- res.status(200).json(result.rows);
- } else {
- res.status(404).json({ message: "User not found" });
- }
+ const database = await connectDatabase();
+
+ const result = await database.query(
+ `SELECT user_id,email,first_name,last_name,avatar FROM users WHERE email='${email}' AND password='${password}';`
+ );
+
+ if (result.rows.length > 0) {
+ res.cookie("user", JSON.stringify(result.rows), {
+ maxAge: 3600000 * 24,
+ httpOnly: false,
+ secure: false,
});
+ res.status(200).json(result.rows);
+ } else {
+ res.status(404).json({ message: "User not found" });
+ }
} catch (error) {
console.error(error);
res.status(500).json({ message: "Internal Server Error" });
@@ -81,28 +77,21 @@ app.post("/login", async (req, res) => {
app.post("/signup", upload.single("avatar"), async (req, res) => {
try {
- // Extract user details from the post request
- const { password, email, firstname, lastname } = req.body;
+ const { password, email, first_name, last_name } = req.body;
- // Verify the user and password are correct from the post request
- if (!password || !email || !firstname || !lastname) {
+ if (!password || !email || !first_name || !last_name) {
res.status(400).json({ message: "Invalid Request" });
- // stop the execution if the username or password is missing
return;
}
- // verify if the email has a good syntax
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(email)) {
res.status(400).json({ message: "Invalid email syntax" });
- // stop the execution if the email is invalid
return;
}
- // connect to the database
const database = await connectDatabase();
- // Verify the user isn't already signup
const emailCheckResult = await database.query(
`SELECT email FROM users WHERE email = '${email}';`
);
@@ -111,17 +100,95 @@ app.post("/signup", upload.single("avatar"), async (req, res) => {
return res.status(400).json({ message: "Email already used" });
}
- // Get the file path after uploading
const avatarPath = path.basename(req.file.path);
console.log(avatarPath);
+ const result = await database.query(
+ `INSERT INTO users (password, email, first_name, last_name, avatar) VALUES ('${password}', '${email}', '${first_name}', '${last_name}','${avatarPath}') RETURNING *;`
+ );
+
+ if (result.rows.length > 0) {
+ res.cookie("user", JSON.stringify(result.rows[0]), {
+ maxAge: 3600000 * 24,
+ httpOnly: false,
+ secure: false,
+ });
+ res
+ .status(200)
+ .json({ message: "User created successfully", user: result.rows[0] });
+ } else {
+ res.status(500).json({ message: "Error creating user" });
+ }
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ message: "Internal Server Error" });
+ }
+});
+
+app.get("/avatar/:id", async (req, res) => {
+ try {
+ const { id } = req.params;
+
+ res.sendFile(path.join(__dirname, `./uploads/${id}`));
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ message: "Internal Server Error" });
+ }
+});
+
+app.get("/currentuser", async (req, res) => {
+ const userCookie = req.cookies.user;
+
+ if (userCookie) {
try {
- // insert the user into the database
+ const user = JSON.parse(userCookie);
+ res.status(200).json({ user: user });
+ } catch (err) {
+ console.error("Error parsing user data", err);
+ res.status(400).json({ message: "Bad Request - Invalid Cookie Data" });
+ }
+ } else {
+ res.status(401).json({ message: "Unauthorized" });
+ }
+});
+
+app.post("/updateUser", async (req, res) => {
+ try {
+ const userCookie = req.cookies.user;
+
+ if (userCookie) {
+ const { password, email, first_name, last_name } = req.body;
+ const user = JSON.parse(userCookie)[0];
+ const user_id = user.user_id;
+
+ if (!user_id || !password || !email || !first_name || !last_name) {
+ res.status(400).json({ message: "Invalid Request" });
+ return;
+ }
+
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
+ if (!emailRegex.test(email)) {
+ res.status(400).json({ message: "Invalid email syntax" });
+ return;
+ }
+
+ const database = await connectDatabase();
+
+ const emailCheckResult = await database.query(
+ `SELECT email FROM users WHERE email = '${email}';`
+ );
+
+ if (emailCheckResult.rows.length > 0) {
+ return res.status(400).json({ message: "Email already used" });
+ }
+
+ const avatarPath = path.basename(req.file.path);
+ console.log(avatarPath);
+
const result = await database.query(
- `INSERT INTO users (password, email, firstname, lastname, avatar_path) VALUES ('${password}', '${email}', '${firstname}', '${lastname}','${avatarPath}') RETURNING *;`
+ `UPDATE users SET password = '${password}', email = '${email}', first_name = '${first_name}', last_name = '${last_name}', avatar = '${avatarPath}' WHERE user_id = '${user_id}' RETURNING *;`
);
- // check if the user was created
if (result.rows.length > 0) {
res.cookie("user", JSON.stringify(result.rows[0]), {
maxAge: 3600000 * 24,
@@ -130,13 +197,12 @@ app.post("/signup", upload.single("avatar"), async (req, res) => {
});
res
.status(200)
- .json({ message: "User created successfully", user: result.rows[0] });
+ .json({ message: "User updated successfully", user: result.rows[0] });
} else {
- res.status(500).json({ message: "Error creating user" });
+ res.status(500).json({ message: "Error updating user" });
}
- } catch (error) {
- console.error(error);
- res.status(500).json({ message: "Database error" });
+ } else {
+ res.status(401).json({ message: "Unauthorized" });
}
} catch (error) {
console.error(error);
@@ -144,109 +210,155 @@ app.post("/signup", upload.single("avatar"), async (req, res) => {
}
});
-app.post("/addPost", async (req, res) => {
+app.post("/updateUserAvatar", upload.single("avatar"), async (req, res) => {
try {
const userCookie = req.cookies.user;
- // check if the user is logged in
+
if (userCookie) {
- // Extract user details from the post request
- const { title, content } = req.body;
- const user_id = userCookie[0].id;
-
- // connect to the database
- database = await connectDatabase();
- // insert the post into the database
- const result = await database
- .query(
- `INSERT INTO posts (user_id, title, content) VALUES ('${user_id}', '${title}', '${content}') RETURNING *;`
- )
- .then((result) => {
- // check if the user was created
- if (result.rows.length > 0) {
- res
- .status(200)
- .json({
- message: "Post created successfully",
- post: result.rows[0],
- });
- } else {
- res.status(500).json({ message: "Error creating post" });
- }
+ const user = JSON.parse(userCookie)[0];
+ const user_id = user.user_id;
+
+ if (!user_id) {
+ res.status(400).json({ message: "Invalid Request" });
+ return;
+ }
+
+ const avatarPath = path.basename(req.file.path);
+ console.log(avatarPath);
+
+ const database = await connectDatabase();
+
+ const result = await database.query(
+ `UPDATE users SET avatar = '${avatarPath}' WHERE user_id = '${user_id}' RETURNING *;`
+ );
+
+ if (result.rows.length > 0) {
+ res.cookie("user", JSON.stringify(result.rows[0]), {
+ maxAge: 3600000 * 24,
+ httpOnly: false,
+ secure: false,
});
- } else
- res
- .status(401)
- .json({ message: "You must be logged in to create a post" });
+ res
+ .status(200)
+ .json({ message: "User updated successfully", user: result.rows[0] });
+ } else {
+ res.status(500).json({ message: "Error updating user" });
+ }
+ } else {
+ res.status(401).json({ message: "Unauthorized" });
+ }
} catch (error) {
console.error(error);
res.status(500).json({ message: "Internal Server Error" });
}
});
-app.post("/addComment", async (req, res) => {
+app.delete("/user", async (req, res) => {
try {
const userCookie = req.cookies.user;
- // check if the user is logged in
+
if (userCookie) {
- // Extract user details from the post request
- const { post_id, content } = req.body;
- const user_id = userCookie[0].id;
+ const user = JSON.parse(userCookie)[0];
+ const user_id = user.user_id;
- // Verify the user and password are correct from the post request
- if (!post_id || !content) {
+ if (!user_id) {
res.status(400).json({ message: "Invalid Request" });
- // stop the execution if the username or password is missing
return;
}
- // connect to the database
- database = await connectDatabase();
- // insert the comment into the database
- const result = await database
- .query(
- `INSERT INTO comments (user_id, post_id, content) VALUES ('${user_id}', '${post_id}', '${content}') RETURNING *;`
- )
- .then((result) => {
- // check if the user was created
- if (result.rows.length > 0) {
- res.status(200).json({
- message: "Comment created successfully",
- comment: result.rows[0],
- });
- } else {
- res.status(500).json({ message: "Error creating comment" });
- }
- });
- } else
- res
- .status(401)
- .json({ message: "You must be logged in to create a comment" });
+ const database = await connectDatabase();
+
+ const result = await database.query(
+ `DELETE FROM users WHERE user_id = '${user_id}' RETURNING *;`
+ );
+
+ if (result.rows.length > 0) {
+ res.clearCookie("user");
+ res.status(200).json({ message: "User deleted successfully", user: result.rows[0] });
+ }
+ } else {
+ res.status(401).json({ message: "Unauthorized" });
+ }
} catch (error) {
console.error(error);
res.status(500).json({ message: "Internal Server Error" });
}
});
+
+app.get("/logout", (req, res) => {
+ res.clearCookie("user");
+ res.status(200).json({ message: "Logged out successfully" });
+});
+
+// ----------------- POSTS ----------------- //
+
app.get("/getPosts", async (req, res) => {
try {
- // connect to the database
- database = await connectDatabase();
-
- const query = `
- SELECT posts.id as id,
- users.firstname as firstName,
- users.lastname as lastName,
- users.id as user_id,
- users.avatar_path as avatar_path,
- posts.content,
- posts.title,
- posts.DATE as timestamp
- FROM posts
- INNER JOIN users on posts.user_id = users.id
- ORDER BY DATE DESC;
- `;
-
- const result = await database.query(query).then((result) => {
+ const userCookie = req.cookies.user;
+
+ if (userCookie) {
+ const database = await connectDatabase();
+
+ const query = `
+ WITH
+ CommentData AS (
+ SELECT
+ comments.post_id,
+ COALESCE(
+ JSON_AGG(
+ JSON_BUILD_OBJECT(
+ 'comment_id', comments.comment_id,
+ 'user_id', comments.user_id,
+ 'first_name', users.first_name,
+ 'last_name', users.last_name,
+ 'content', comments.content,
+ 'created_at', comments.created_at
+ )
+ ) FILTER (WHERE comments.comment_id IS NOT NULL),
+ '[]'
+ ) AS comments
+ FROM
+ comments
+ LEFT JOIN
+ users ON comments.user_id = users.user_id
+ GROUP BY
+ comments.post_id
+ ),
+ LikeData AS (
+ SELECT
+ post_id,
+ COUNT(user_id) AS like_count
+ FROM
+ likes
+ GROUP BY
+ post_id
+ )
+ SELECT
+ posts.post_id,
+ posts.user_id,
+ users.first_name,
+ users.last_name,
+ users.avatar,
+ posts.title,
+ posts.content,
+ posts.created_at,
+ COALESCE(CommentData.comments, '[]') AS comments,
+ COALESCE(LikeData.like_count, 0) AS like_count
+ FROM
+ posts
+ INNER JOIN
+ users ON posts.user_id = users.user_id
+ LEFT JOIN
+ CommentData ON posts.post_id = CommentData.post_id
+ LEFT JOIN
+ LikeData ON posts.post_id = LikeData.post_id
+ ORDER BY
+ posts.created_at DESC;
+ `;
+
+ const result = await database.query(query);
+
if (result.rows.length > 0) {
res.status(200).json({
message: "Posts retrieved successfully",
@@ -255,111 +367,139 @@ app.get("/getPosts", async (req, res) => {
} else {
res.status(404).json({ message: "No posts found" });
}
- });
+ }
} catch (error) {
console.error(error);
res.status(500).json({ message: "Internal Server Error" });
}
});
-app.get("/getComments", async (req, res) => {
+app.post("/write", async (req, res) => {
try {
- // get the parameters from the request
- const { post_id } = req.query;
-
- // connect to the database
- database = await connectDatabase();
- // get the comments of the post from the database
- const result = await database
- .query(`SELECT * FROM comments WHERE post_id='${post_id}';`)
- .then((result) => {
- // check if the user was created
- if (result.rows.length > 0) {
- res.status(200).json({
- message: "Comments retrieved successfully",
- comments: result.rows,
- });
- } else {
- res.status(500).json({ message: "Error retrieving comments" });
- }
- });
+ const userCookie = req.cookies.user;
+
+ if (userCookie) {
+ const { title, content } = req.body;
+ const user = JSON.parse(userCookie)[0];
+ const user_id = user.user_id;
+
+ if (!user_id || !title || !content) {
+ res.status(400).json({ message: "Invalid Request" });
+ return;
+ }
+
+ const database = await connectDatabase();
+
+ const query = `
+ INSERT INTO posts (user_id, title, content)
+ VALUES ($1, $2, $3)
+ RETURNING *;
+ `;
+
+ const values = [user_id, title, content];
+
+ const result = await database.query(query, values);
+
+ if (result.rows.length > 0) {
+ res.status(200).json({
+ message: "Post created successfully",
+ post: result.rows[0],
+ });
+ } else {
+ res.status(500).json({ message: "Error creating post" });
+ }
+ } else {
+ res
+ .status(401)
+ .json({ message: "You must be logged in to create a post" });
+ }
} catch (error) {
console.error(error);
res.status(500).json({ message: "Internal Server Error" });
}
});
-app.post("/likePost", async (req, res) => {
+app.post("/addComment", async (req, res) => {
try {
const userCookie = req.cookies.user;
- // Check if the user is logged in
+
if (userCookie) {
- // Extract user details from the post request
- const { user_id, post_id } = req.body;
+ const { post_id, content } = req.body;
+ const user = JSON.parse(userCookie)[0];
+ const user_id = user.user_id;
- // Verify the user and post IDs are provided
- if (!user_id || !post_id) {
- return res.status(400).json({ message: "Invalid Request" });
+ if (!post_id || !content) {
+ res.status(400).json({ message: "Invalid Request" });
+ return;
}
- // connect to the database
- database = await connectDatabase();
- // Insert the like into the database without using parameterized query
+
+ const database = await connectDatabase();
const result = await database.query(
- `INSERT INTO Likes (user_id, post_id) VALUES ('${user_id}', '${post_id}') RETURNING *;`
+ `INSERT INTO comments (user_id, post_id, content) VALUES ($1, $2, $3) RETURNING *;`,
+ [user_id, post_id, content]
);
- // Check if the like was created
if (result.rows.length > 0) {
- return res
- .status(200)
- .json({ message: "Like created successfully", like: result.rows[0] });
+ res.status(200).json({
+ message: "Comment created successfully",
+ comment: result.rows[0],
+ });
} else {
- return res.status(500).json({ message: "Error creating like" });
+ res.status(500).json({ message: "Error creating comment" });
}
} else {
- return res
+ res
.status(401)
- .json({ message: "You must be logged in to create a like" });
+ .json({ message: "You must be logged in to create a comment" });
}
} catch (error) {
console.error(error);
-
- // Check if error is due to a unique constraint violation
- if (error.code === "23505") {
- return res.status(400).json({ message: "User already liked this post" });
- }
-
- return res.status(500).json({ message: "Internal Server Error" });
+ res.status(500).json({ message: "Internal Server Error" });
}
});
app.post("/likePost", async (req, res) => {
try {
const userCookie = req.cookies.user;
- // Check if the user is logged in
+
if (userCookie) {
- // Extract user details from the post request
const { post_id } = req.body;
- const user_id = userCookie[0].id;
+ const user = JSON.parse(userCookie)[0];
+ const user_id = user.user_id;
- // Verify the user and post IDs are provided
if (!post_id) {
return res.status(400).json({ message: "Invalid Request" });
}
- // connect to the database
- database = await connectDatabase();
- // Insert the like into the database without using parameterized query
- const result = await database.query(
- `INSERT INTO Likes (user_id, post_id) VALUES ('${user_id}', '${post_id}') RETURNING *;`
- );
- // Check if the like was created
- if (result.rows.length > 0) {
- return res
- .status(200)
- .json({ message: "Like created successfully", like: result.rows[0] });
+ const database = await connectDatabase();
+ const checkQuery =
+ "SELECT * FROM likes WHERE user_id = $1 AND post_id = $2;";
+ const checkResult = await database.query(checkQuery, [user_id, post_id]);
+
+ if (checkResult.rows.length > 0) {
+ const deleteQuery =
+ "DELETE FROM likes WHERE user_id = $1 AND post_id = $2 RETURNING *;";
+ const deleteResult = await database.query(deleteQuery, [
+ user_id,
+ post_id,
+ ]);
+
+ return res.status(200).json({
+ message: "Like removed successfully",
+ like: deleteResult.rows[0],
+ });
} else {
- return res.status(500).json({ message: "Error creating like" });
+ const insertQuery =
+ "INSERT INTO likes (user_id, post_id) VALUES ($1, $2) RETURNING *;";
+ const insertResult = await database.query(insertQuery, [
+ user_id,
+ post_id,
+ ]);
+
+ return res.status(200).json({
+ message: "Like created successfully",
+ like: insertResult.rows[0],
+ });
}
} else {
return res
@@ -368,55 +508,10 @@ app.post("/likePost", async (req, res) => {
}
} catch (error) {
console.error(error);
-
- // Check if error is due to a unique constraint violation
- if (error.code === "23505") {
- return res.status(400).json({ message: "User already liked this post" });
- }
-
return res.status(500).json({ message: "Internal Server Error" });
}
});
-app.get("/logout", (req, res) => {
- // Clear the user cookie; the name 'user' should match the name used when the cookie was set in the login route.
- res.clearCookie("user");
- // Sending a successful response. In a real-world scenario, additional cleanup or checks might be necessary.
- res.status(200).json({ message: "Logged out successfully" });
-});
-
-app.get("/avatar/:id", async (req, res) => {
- try {
- const { id } = req.params;
-
- res.sendFile(path.join(__dirname, `./uploads/${id}`));
-
- } catch (error) {
- console.error(error);
- res.status(500).json({ message: "Internal Server Error" });
- }
-});
-
-app.get("/currentuser", (req, res) => {
- // Attempt to retrieve the user data from the cookie instead of the session.
- // This is insecure because user data is exposed, and cookies can be manipulated on the client-side.
- const userCookie = req.cookies.user;
-
- if (userCookie) {
- let user;
- try {
- user = JSON.parse(userCookie);
- res.status(200).json({ user: user });
- } catch (err) {
- console.error("Error parsing user data", err);
- res.status(400).json({ message: "Bad Request - Invalid Cookie Data" });
- }
- } else {
- // No cookie means that the user is not authenticated.
- res.status(401).json({ message: "Unauthorized" });
- }
-});
-
app.use("/api/v1", api);
app.use(notFound);
app.use(errorHandler);
diff --git a/src/database/fixtures.js b/src/database/fixtures.js
index 753d400..24149ce 100644
--- a/src/database/fixtures.js
+++ b/src/database/fixtures.js
@@ -1,80 +1,79 @@
-require('dotenv').config();
-const {connectDatabase} = require('./connectionconfigDb');
+require("dotenv").config();
+const { connectDatabase } = require("./connectionconfigDb");
const seedDatabase = async () => {
- const client = await connectDatabase();
+ const client = await connectDatabase();
- const insertUsers = `
- INSERT INTO users (password, email, firstname, lastname, avatar_path)
- VALUES
- ('password1', 'john.doe@example.com', 'John', 'Doe', 'eren_avatar.jpg'),
- ('password2', 'jane.doe@example.com', 'Jane', 'Doe', 'eren_avatar.jpg'),
- ('password3', 'will.smith@example.com', 'Will', 'Smith', 'eren_avatar.jpg'),
- ('password4', 'sarah.connor@example.com', 'Sarah', 'Connor', 'eren_avatar.jpg'),
- ('password5', 'mary.jane@example.com', 'Mary', 'Jane', 'eren_avatar.jpg'),
- ('password6', 'tony.stark@example.com', 'Tony', 'Stark', 'eren_avatar.jpg'),
- ('password7', 'peter.parker@example.com', 'Peter', 'Parker', 'eren_avatar.jpg'),
- ('password8', 'bruce.wayne@example.com', 'Bruce', 'Wayne', 'eren_avatar.jpg');
- `;
- await client.query(insertUsers);
+ const insertUsers = `
+ INSERT INTO users (password, email, first_name, last_name, avatar)
+ VALUES
+ ('password1', 'john.doe@example.com', 'John', 'Doe', 'eren_avatar.jpg'),
+ ('password2', 'jane.doe@example.com', 'Jane', 'Doe', 'eren_avatar.jpg'),
+ ('password3', 'will.smith@example.com', 'Will', 'Smith', 'eren_avatar.jpg'),
+ ('password4', 'sarah.connor@example.com', 'Sarah', 'Connor', 'eren_avatar.jpg'),
+ ('password5', 'mary.jane@example.com', 'Mary', 'Jane', 'eren_avatar.jpg'),
+ ('password6', 'tony.stark@example.com', 'Tony', 'Stark', 'eren_avatar.jpg'),
+ ('password7', 'peter.parker@example.com', 'Peter', 'Parker', 'eren_avatar.jpg'),
+ ('password8', 'bruce.wayne@example.com', 'Bruce', 'Wayne', 'eren_avatar.jpg');
+`;
+ await client.query(insertUsers);
- const insertPosts = `
- INSERT INTO posts (user_id, title, content, DATE)
- VALUES
- (1, 'My First Post', 'This is my first post content', '2023-10-01 08:00:00'),
- (1, 'My Second Post', 'This is my second post content', '2023-10-02 09:00:00'),
- (2, 'Jane''s Thoughts', 'Random musings', '2023-10-03 10:00:00'),
- (3, 'Tech Tips', 'Some useful tech tips', '2023-10-04 11:00:00'),
- (4, 'Nature Love', 'Why we should love nature', '2023-10-05 12:00:00'),
- (5, 'The Importance of Sleep', 'Sleep is crucial for health', '2023-10-06 13:00:00'),
- (6, 'My New Invention', 'I just invented something cool', '2023-10-07 14:00:00'),
- (7, 'My Photography Journey', 'How I got into photography', '2023-10-08 15:00:00'),
- (8, 'Batman vs Superman', 'Who would win?', '2023-10-09 16:00:00');
- `;
- await client.query(insertPosts);
+ const insertPosts = `
+ INSERT INTO posts (user_id, title, content, created_at)
+ VALUES
+ (1, 'My First Post', 'This is my first post content', '2023-10-01 08:00:00'),
+ (1, 'My Second Post', 'This is my second post content', '2023-10-02 09:00:00'),
+ (2, 'Jane''s Thoughts', 'Random musings', '2023-10-03 10:00:00'),
+ (3, 'Tech Tips', 'Some useful tech tips', '2023-10-04 11:00:00'),
+ (4, 'Nature Love', 'Why we should love nature', '2023-10-05 12:00:00'),
+ (5, 'The Importance of Sleep', 'Sleep is crucial for health', '2023-10-06 13:00:00'),
+ (6, 'My New Invention', 'I just invented something cool', '2023-10-07 14:00:00'),
+ (7, 'My Photography Journey', 'How I got into photography', '2023-10-08 15:00:00'),
+ (8, 'Batman vs Superman', 'Who would win?', '2023-10-09 16:00:00');
+`;
+ await client.query(insertPosts);
- const insertComments = `
- INSERT INTO comments (user_id, post_id, content)
- VALUES
- (1, 1, 'Great post!'),
- (2, 1, 'Thanks for sharing'),
- (3, 2, 'Very insightful'),
- (4, 3, 'I learned a lot'),
- (1, 4, 'Love this!'),
- (2, 5, 'Nature is wonderful'),
- (3, 6, 'Tell us more about your invention'),
- (4, 6, 'That sounds amazing'),
- (5, 7, 'Photography is an art'),
- (6, 8, 'Team Batman all the way'),
- (7, 8, 'Superman would definitely win');
- `;
- await client.query(insertComments);
+ const insertComments = `
+ INSERT INTO comments (user_id, post_id, content)
+ VALUES
+ (1, 1, 'Great post!'),
+ (2, 1, 'Thanks for sharing'),
+ (3, 2, 'Very insightful'),
+ (4, 3, 'I learned a lot'),
+ (1, 4, 'Love this!'),
+ (2, 5, 'Nature is wonderful'),
+ (3, 6, 'Tell us more about your invention'),
+ (4, 6, 'That sounds amazing'),
+ (5, 7, 'Photography is an art'),
+ (6, 8, 'Team Batman all the way'),
+ (7, 8, 'Superman would definitely win');
+`;
+ await client.query(insertComments);
- const insertLikes = `
- INSERT INTO likes (post_id, user_id)
- VALUES
- (1, 1),
- (1, 2),
- (2, 3),
- (3, 4),
- (2, 1),
- (3, 2),
- (4, 1),
- (5, 2),
- (6, 4),
- (4, 3),
- (7, 5),
- (8, 6),
- (5, 5),
- (6, 7),
- (6, 8),
- (7, 7);
- `;
- await client.query(insertLikes);
+ const insertLikes = `
+ INSERT INTO likes (post_id, user_id)
+ VALUES
+ (1, 1),
+ (1, 2),
+ (2, 3),
+ (3, 4),
+ (2, 1),
+ (4, 1),
+ (5, 2),
+ (6, 4),
+ (4, 3),
+ (7, 5),
+ (8, 6),
+ (5, 5),
+ (6, 7),
+ (6, 8),
+ (7, 7);
+`;
+ await client.query(insertLikes);
- console.log('Random data done ✅');
+ console.log("Random data done ✅");
- await client.end();
+ await client.end();
};
-seedDatabase().catch(err => console.error('Error seeding database:', err));
+seedDatabase().catch((err) => console.error("Error seeding database:", err));
diff --git a/src/database/resetDb.js b/src/database/resetDb.js
index a3e2a1d..51e313c 100644
--- a/src/database/resetDb.js
+++ b/src/database/resetDb.js
@@ -2,18 +2,15 @@ const { Client } = require('pg');
require('dotenv').config();
const resetDatabase = async () => {
- // Create a client for database deletion
const rootClient = new Client({
host: process.env.HOST_DATABASE,
port: process.env.PORT_DATABASE,
user: process.env.USER_DATABASE,
password: process.env.PASSWORD_DATABASE,
- // Connect to the main database when deleting the hackme database
database: process.env.BIG_DATABASE
});
await rootClient.connect();
- // Drop the hackme database
await rootClient.query(`DROP DATABASE IF EXISTS hackme;`)
.then(() => console.log('Database deleted successfully'))
.catch(err => console.error('Error deleting database:', err));
diff --git a/src/database/setupDb.js b/src/database/setupDb.js
index cb62491..562759b 100644
--- a/src/database/setupDb.js
+++ b/src/database/setupDb.js
@@ -26,46 +26,46 @@ const createDatabase = async () => {
// Create a client for the new database
const client = await connectDatabase();
- // drop all tables
- // await client.query('DROP TABLE users, posts, comments;');
-
// Create tables
- const createUsersTable = ` CREATE TABLE users (
- id SERIAL PRIMARY KEY,
+ const createUserTable = `CREATE TABLE users (
+ user_id SERIAL PRIMARY KEY,
password VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
- firstname VARCHAR(255) NOT NULL,
- lastname VARCHAR(255) NOT NULL,
- avatar_path VARCHAR(255) NOT NULL
+ first_name VARCHAR(255) NOT NULL,
+ last_name VARCHAR(255) NOT NULL,
+ avatar VARCHAR(255) NOT NULL
);`;
- const createPostsTable = `CREATE TABLE posts (
- id SERIAL PRIMARY KEY,
+ const createPostTable = `CREATE TABLE posts (
+ post_id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
- FOREIGN KEY (user_id) REFERENCES users(id),
- DATE TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
);`;
- const createCommentsTable = `CREATE TABLE comments (
- id SERIAL PRIMARY KEY,
+ const createCommentTable = `CREATE TABLE comments (
+ comment_id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
post_id INTEGER NOT NULL,
content TEXT NOT NULL,
- FOREIGN KEY (user_id) REFERENCES users(id),
- FOREIGN KEY (post_id) REFERENCES posts(id),
- DATE TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
+ FOREIGN KEY (post_id) REFERENCES posts(post_id) ON DELETE CASCADE
);`;
- const createLikes = `CREATE TABLE Likes (
- id SERIAL PRIMARY KEY,
- post_id INTEGER REFERENCES posts(id),
- user_id INTEGER REFERENCES users(id),
- UNIQUE(user_id, post_id)
+ const createLikeTable = `
+ CREATE TABLE likes (
+ like_id SERIAL PRIMARY KEY,
+ post_id INTEGER NOT NULL,
+ user_id INTEGER NOT NULL,
+ FOREIGN KEY (post_id) REFERENCES posts(post_id) ON DELETE CASCADE,
+ FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
+ UNIQUE (user_id, post_id)
);`;
- await client.query(createUsersTable);
- await client.query(createPostsTable);
- await client.query(createCommentsTable);
- await client.query(createLikes);
+ await client.query(createUserTable);
+ await client.query(createPostTable);
+ await client.query(createCommentTable);
+ await client.query(createLikeTable);
await client.end().then(() => console.log('Database created successfully'));
};