Skip to content

Commit 9949228

Browse files
authored
Merge pull request #69 from acm-uic/createComment
Create comment endpoint
2 parents 5f0256d + adfe7ed commit 9949228

File tree

18 files changed

+393
-49
lines changed

18 files changed

+393
-49
lines changed

backend/.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
PORT =
22
MONGODB =
3-
SECRET_KEY =
3+
AT_SECRET_KEY =
4+
RT_SECRET_KEY =
45
CLOUDINARY_CONFIG =

backend/config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import dotenv from "dotenv";
22
dotenv.config({ path: ".env.local" });
33

4-
const SECRET_KEY = process.env.SECRET_KEY;
4+
const AT_SECRET_KEY = process.env.AT_SECRET_KEY;
5+
const RT_SECRET_KEY = process.env.RT_SECRET_KEY;
56
const CLOUDINARY_CONFIG = JSON.parse(process.env.CLOUDINARY_CONFIG);
67
const MONGODB = process.env.MONGODB;
78

8-
export { SECRET_KEY, CLOUDINARY_CONFIG, MONGODB };
9+
export { AT_SECRET_KEY, RT_SECRET_KEY, CLOUDINARY_CONFIG, MONGODB };

backend/controllers/comment.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import PostModel from "../models/post.js";
2+
import CommentModel from "../models/comment.js";
3+
4+
const CommentControllers = {
5+
createComment: async (req, res) => {
6+
try {
7+
// Get postId, body, and user transferred from req
8+
const { postId, body } = req.body;
9+
const { user } = req;
10+
// Find post from postId
11+
const crrPost = await PostModel.findById(postId);
12+
// If cannot find, throw an error
13+
if (!crrPost) throw new Error("Cannot not find post!");
14+
// Create a new comment
15+
const newComment = await CommentModel.create({
16+
author: user._id,
17+
postId,
18+
body,
19+
});
20+
// Send out the new comment and the userName of the author
21+
res.status(201).send({
22+
message: "Comment created successfully!",
23+
success: true,
24+
data: {
25+
...newComment.toObject(),
26+
userName: user.userName,
27+
},
28+
});
29+
} catch (error) {
30+
res.status(404).send({
31+
message: error.message,
32+
success: false,
33+
data: null,
34+
});
35+
}
36+
},
37+
};
38+
39+
export default CommentControllers;

backend/controllers/post.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import PostModel from "../models/post.js";
2+
import { handleFileUpload } from "../utils/upload.js";
3+
4+
const PostControllers = {
5+
createPost: async (req, res) => {
6+
try {
7+
const { user } = req;
8+
const { title, body } = req.body;
9+
const listFile = req.files;
10+
11+
const listMedia = [];
12+
13+
// If user upload files
14+
for (const file of listFile) {
15+
const response = await handleFileUpload(file);
16+
if (!response.success) throw new Error(response.message);
17+
listMedia.push(response.data);
18+
}
19+
20+
const newPost = await PostModel.create({
21+
author: user._id,
22+
title,
23+
body,
24+
images: listMedia,
25+
});
26+
27+
res.status(201).send({
28+
message: "Post created successfully",
29+
success: true,
30+
data: {
31+
...newPost.toObject(),
32+
userName: user.userName,
33+
},
34+
});
35+
} catch (error) {
36+
res.status(500).send({
37+
message: error.message,
38+
success: false,
39+
data: null,
40+
});
41+
}
42+
},
43+
};
44+
45+
export default PostControllers;

backend/controllers/user.js

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import UserModel from "../models/user.js";
22
import bcrypt from "bcrypt";
3-
import jwt from "jsonwebtoken";
4-
import { SECRET_KEY, CLOUDINARY_CONFIG } from "../config.js";
3+
import { CLOUDINARY_CONFIG } from "../config.js";
54
import { v2 as cloudinary } from "cloudinary";
6-
import { handleAvatarUpload } from "../utils/avatarHandlers.js";
5+
import { handleFileUpload } from "../utils/upload.js";
6+
import { generateToken } from "../utils/token.js";
77

88
cloudinary.config(CLOUDINARY_CONFIG);
99

@@ -34,8 +34,9 @@ const UserControllers = {
3434
};
3535
// Only add avatar if the user upload avatar file
3636
if (avatar) {
37-
const response = await handleAvatarUpload(avatar, userPayload);
37+
const response = await handleFileUpload(avatar);
3838
if (!response.success) throw new Error(response.message);
39+
userPayload.avatar = response.data;
3940
}
4041

4142
// Create a new user
@@ -80,17 +81,35 @@ const UserControllers = {
8081
const user = {
8182
_id: crrUser._id,
8283
email: crrUser.email,
84+
userName: crrUser.userName,
8385
};
8486

85-
const token = jwt.sign(user, SECRET_KEY, { expiresIn: 60 * 60 });
87+
const accessToken = generateToken(
88+
{
89+
...user,
90+
typeToken: "AT",
91+
},
92+
"AT"
93+
);
94+
95+
const refreshToken = generateToken(
96+
{
97+
...user,
98+
typeToken: "RT",
99+
},
100+
"RT"
101+
);
86102

87103
res.status(200).send({
88104
message: "User signs in successfully",
89105
success: true,
90-
data: token,
106+
data: {
107+
accessToken,
108+
refreshToken,
109+
},
91110
});
92111
} catch (error) {
93-
res.status(409).send({
112+
res.status(400).send({
94113
message: error.message,
95114
success: false,
96115
data: null,
@@ -103,11 +122,6 @@ const UserControllers = {
103122
const { userName, email, bio } = req.body;
104123
const avatar = req.file;
105124

106-
// If the user doesn't update anything
107-
if (!(userName || email || avatar || bio)) {
108-
throw new Error("Please enter an updated field!");
109-
}
110-
111125
// Get the crrUser
112126
const crrUser = await UserModel.findById(user._id);
113127

@@ -134,8 +148,9 @@ const UserControllers = {
134148
// If the names are different, replace with the new file
135149
if (newFileName !== currentFileName) {
136150
// Proceed with upload
137-
const response = await handleAvatarUpload(avatar, updatedFields);
151+
const response = await handleFileUpload(avatar);
138152
if (!response.success) throw new Error(response.message);
153+
updatedFields.avatar = response.data;
139154
// Delete the old avatar from Cloudinary
140155
if (currentFileName) {
141156
await cloudinary.uploader.destroy(currentFileName);
@@ -155,7 +170,7 @@ const UserControllers = {
155170
data: updatedProfile,
156171
});
157172
} catch (error) {
158-
res.status(400).send({
173+
res.status(500).send({
159174
message: error.message,
160175
success: false,
161176
data: null,

backend/database/collections.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
const Collections = {
2-
users: "users",
2+
users: "User",
3+
posts: "Post",
4+
comments: "Comment",
35
};
46

57
export default Collections;

backend/middlewares/auth.js

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,73 @@
1-
import jwt from "jsonwebtoken";
2-
import { SECRET_KEY } from "../config.js";
1+
import { verifyToken } from "../utils/token.js";
32

43
const AuthMiddlewares = {
5-
validateToken: (req, res, next) => {
6-
const authHeader = req.headers["authorization"];
7-
if (authHeader) {
4+
verifyAccessToken: (req, res, next) => {
5+
try {
6+
const authHeader = req.headers["authorization"];
7+
if (!authHeader) throw new Error("Please enter token");
88
// Split the access token from the bearer token
99
const token = authHeader.split(" ")[1];
1010

1111
// Validate the access token
12-
jwt.verify(token, SECRET_KEY, (err, decoded) => {
13-
if (err) {
14-
res.status(401).send({
15-
message: "Access token is invalid",
16-
success: false,
17-
data: null,
18-
});
19-
} else {
20-
// Save user info into req,
21-
// Req will be transferred to next handlers
22-
req.user = decoded;
23-
return next();
24-
}
12+
const data = verifyToken(token, "AT");
13+
req.user = data;
14+
return next();
15+
} catch (error) {
16+
let type = "";
17+
let getMessage = "";
18+
switch (error.message) {
19+
case "invalid signature":
20+
getMessage = "Cannot verify token";
21+
type = "INVALID_TOKEN";
22+
break;
23+
case "jwt expired":
24+
getMessage = "Token is expired";
25+
type = "EXP_TOKEN";
26+
break;
27+
default:
28+
getMessage = "Cannot authenticate user";
29+
type = "UNAUTH";
30+
break;
31+
}
32+
res.status(401).send({
33+
message: getMessage,
34+
type,
35+
success: false,
36+
data: null,
2537
});
26-
} else {
38+
}
39+
},
40+
verifyRefreshToken: (req, res, next) => {
41+
try {
42+
const authHeader = req.headers["authorization"];
43+
if (!authHeader) throw new Error("Please enter token");
44+
// Split the access token from the bearer token
45+
const token = authHeader.split(" ")[1];
46+
47+
// Validate the access token
48+
const data = verifyToken(token, "RT");
49+
req.user = data;
50+
return next();
51+
} catch (error) {
52+
let type = "";
53+
let getMessage = "";
54+
switch (error.message) {
55+
case "invalid signature":
56+
getMessage = "Cannot verify token";
57+
type = "INVALID_TOKEN";
58+
break;
59+
case "jwt expired":
60+
getMessage = "Token is expired";
61+
type = "EXP_TOKEN";
62+
break;
63+
default:
64+
getMessage = "Cannot authenticate user";
65+
type = "UNAUTH";
66+
break;
67+
}
2768
res.status(401).send({
28-
message: "Access token is missing",
69+
message: getMessage,
70+
type,
2971
success: false,
3072
data: null,
3173
});

backend/middlewares/comment.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const CommentMiddlewares = {
2+
// Middleware for creating a comment
3+
createComment: (req, res, next) => {
4+
try {
5+
// Ask for postId and comment body
6+
const { postId, body } = req.body;
7+
// If lack either, throw an error
8+
if (!postId) throw new Error("Please enter postId!");
9+
if (!body) throw new Error("Please enter your comment!");
10+
11+
return next();
12+
} catch (error) {
13+
res.status(400).send({
14+
message: error.message,
15+
success: false,
16+
data: null,
17+
});
18+
}
19+
},
20+
};
21+
22+
export default CommentMiddlewares;

backend/middlewares/post.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const PostMiddlewares = {
2+
// A middleware for creating a new post
3+
createUser: (req, res, next) => {
4+
try {
5+
const { title, body } = req.body;
6+
// Throw error if missing title or body
7+
if (!title) throw new Error("Please enter post title!");
8+
if (!body) throw new Error("Please enter post body!");
9+
10+
return next();
11+
} catch (error) {
12+
res.status(400).send({
13+
message: error.message,
14+
success: false,
15+
data: null,
16+
error,
17+
});
18+
}
19+
},
20+
};
21+
22+
export default PostMiddlewares;

backend/middlewares/user.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@ const UserMiddlewares = {
2929
if (!email) throw new Error("Please enter email!");
3030
if (!password) throw new Error("Please enter password!");
3131

32+
return next();
33+
} catch (error) {
34+
res.status(400).send({
35+
message: error.message,
36+
success: false,
37+
data: null,
38+
});
39+
}
40+
},
41+
updateProfile: (req, res, next) => {
42+
try {
43+
const { userName, email, bio } = req.body;
44+
const avatar = req.file;
45+
46+
// If the user doesn't update anything
47+
if (!(userName || email || avatar || bio)) {
48+
throw new Error("Please enter an updated field!");
49+
}
50+
3251
return next();
3352
} catch (error) {
3453
res.status(400).send({

0 commit comments

Comments
 (0)