Skip to content

Commit 4be997d

Browse files
committed
part 7
1 parent 90b3707 commit 4be997d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+36121
-0
lines changed

part7/extended-blog/app.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const express = require("express");
2+
require("express-async-errors");
3+
const app = express();
4+
const cors = require("cors");
5+
const mongoose = require("mongoose");
6+
const config = require("./utils/config");
7+
const blogRouter = require("./controllers/blog");
8+
const userRouter = require("./controllers/users");
9+
const morgan = require("morgan");
10+
const middleware = require("./utils/middleware");
11+
const loginRouter = require("./controllers/login");
12+
13+
morgan.token("body", function (req, res) {
14+
return JSON.stringify(req.body);
15+
});
16+
17+
mongoose.connect(config.mongoUrl, {
18+
useNewUrlParser: true,
19+
useUnifiedTopology: true,
20+
useFindAndModify: false,
21+
useCreateIndex: true,
22+
});
23+
24+
app.use(cors());
25+
app.use(express.json());
26+
app.use(
27+
morgan(":method :url :status :res[content-length] - :response-time ms :body")
28+
);
29+
app.use("/api/blogs", blogRouter);
30+
app.use("/api/users", userRouter);
31+
app.use("/api/login", loginRouter);
32+
if (process.env.NODE_ENV === "test") {
33+
const testingRouter = require("./controllers/testing");
34+
app.use("/api/testing", testingRouter);
35+
}
36+
app.use(middleware.unknownEndpoint);
37+
app.use(middleware.errorHandler);
38+
39+
module.exports = app;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
require("dotenv").config();
2+
const blogRouter = require("express").Router();
3+
const Blog = require("../models/blog");
4+
const User = require("../models/user");
5+
const Comment = require("../models/comment");
6+
const jwt = require("jsonwebtoken");
7+
const { tokenExtractor } = require("../utils/middleware");
8+
9+
blogRouter
10+
.route("/")
11+
.get(async (req, res) => {
12+
const blogs = await Blog.find({}).populate("user").populate("comments");
13+
res.json(blogs);
14+
})
15+
.post(tokenExtractor, async (req, res) => {
16+
const { author, title, url, likes } = req.body;
17+
18+
const user = await User.findById(req.user);
19+
20+
if (!title || !url) res.status(400).end();
21+
22+
const blog = new Blog({
23+
author: author || "unknown",
24+
title,
25+
url,
26+
likes: likes || 0,
27+
user: user._id,
28+
});
29+
30+
const savedBlog = await blog.save();
31+
user.blogs = [...user.blogs, savedBlog._id];
32+
await user.save();
33+
res.status(201).json(savedBlog);
34+
});
35+
36+
blogRouter
37+
.route("/:id")
38+
.get(async (req, res) => {
39+
const blog = await Blog.findById(req.params.id);
40+
if (blog) {
41+
res.json(blog.toJSON());
42+
} else {
43+
res.status(404).end();
44+
}
45+
})
46+
.delete(tokenExtractor, async (req, res) => {
47+
const { user } = req;
48+
const blog = await Blog.findById(req.params.id);
49+
50+
if (blog === null) return res.status(400).end();
51+
52+
if (blog.user.toString() === user) {
53+
await Blog.findByIdAndRemove(req.params.id);
54+
res.status(204).end();
55+
} else {
56+
res.status(400).end();
57+
}
58+
})
59+
.put(async (req, res) => {
60+
const { comments, ...blog } = req.body;
61+
const updatedBlog = await Blog.findByIdAndUpdate(req.params.id, blog, {
62+
new: true,
63+
});
64+
res.json(updatedBlog);
65+
});
66+
67+
blogRouter.route("/:id/comments").post(async (req, res) => {
68+
const blogId = req.params.id;
69+
const content = req.body.comment;
70+
71+
const blog = await Blog.findById(blogId);
72+
73+
if (!content) res.status(400).end();
74+
75+
const comment = new Comment({
76+
content,
77+
blog: blogId,
78+
});
79+
80+
const savedComment = await comment.save();
81+
blog.comments = [...blog.comments, comment._id];
82+
await blog.save();
83+
res.status(201).json(savedComment);
84+
});
85+
86+
module.exports = blogRouter;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require("dotenv").config();
2+
const router = require("express").Router();
3+
const jwt = require("jsonwebtoken");
4+
const bcrypt = require("bcrypt");
5+
const User = require("../models/user");
6+
7+
router.route("/").post(async (req, res) => {
8+
const { username, password } = req.body;
9+
10+
const user = await User.findOne({ username });
11+
const verifyPassword =
12+
user !== null && (await bcrypt.compare(password, user.passwordHash));
13+
14+
if (!(user && verifyPassword)) {
15+
return res.status(401).json({
16+
error: "invalid username or password",
17+
});
18+
}
19+
20+
const token = jwt.sign(
21+
{ username: user.username, id: user._id },
22+
process.env.SECRET,
23+
{ expiresIn: "1hr" }
24+
);
25+
26+
res.status(200).send({ token, username: user.username, name: user.name });
27+
});
28+
29+
module.exports = router;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
var router = require("express").Router();
2+
var Blog = require("../models/blog");
3+
var User = require("../models/user");
4+
5+
router.post("/reset", async (req, res) => {
6+
await Blog.deleteMany({});
7+
await User.deleteMany({});
8+
9+
res.status(204).end();
10+
});
11+
12+
module.exports = router;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const router = require("express").Router();
2+
const User = require("../models/user");
3+
const bcrypt = require("bcrypt");
4+
5+
router
6+
.route("/")
7+
.get(async (req, res) => {
8+
const users = await User.find({}).populate("blogs");
9+
res.json(users);
10+
})
11+
.post(async (req, res) => {
12+
const salt = await bcrypt.genSalt(10);
13+
const passwordHash = await bcrypt.hash(req.body.password, salt);
14+
15+
const newUser = new User({
16+
...req.body,
17+
passwordHash,
18+
});
19+
const result = await newUser.save();
20+
21+
res.status(201).json(result);
22+
});
23+
24+
router.route("/:id").get(async (req, res) => {
25+
const user = await User.findById(req.params.id).populate("blogs");
26+
if (user) {
27+
res.json(user.toJSON());
28+
} else {
29+
res.status(404).end();
30+
}
31+
});
32+
33+
module.exports = router;

part7/extended-blog/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const app = require("./app");
2+
const logger = require("./utils/logger")
3+
const config = require("./utils/config")
4+
5+
app.listen(config.PORT, () => {
6+
logger.info(`Server running on port ${config.PORT}`);
7+
});

part7/extended-blog/jest.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
testEnvironment: "node",
3+
};

part7/extended-blog/models/blog.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const mongoose = require("mongoose");
2+
const uniqueValidator = require("mongoose-unique-validator");
3+
4+
const blogSchema = new mongoose.Schema({
5+
title: { type: String },
6+
author: { type: String },
7+
url: String,
8+
likes: { type: Number },
9+
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }],
10+
user: {
11+
type: mongoose.Schema.Types.ObjectId,
12+
ref: "User",
13+
},
14+
});
15+
16+
blogSchema.plugin(uniqueValidator);
17+
18+
blogSchema.set("toJSON", {
19+
transform: (document, returnedObject) => {
20+
returnedObject.id = returnedObject._id.toString();
21+
delete returnedObject._id;
22+
delete returnedObject.__v;
23+
},
24+
});
25+
26+
module.exports = mongoose.model("Blog", blogSchema);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const mongoose = require("mongoose");
2+
const uniqueValidator = require("mongoose-unique-validator");
3+
4+
const commentSchema = new mongoose.Schema(
5+
{
6+
content: String,
7+
blog: {
8+
type: mongoose.Schema.Types.ObjectId,
9+
ref: "Blog",
10+
},
11+
},
12+
{ timestamps: true }
13+
);
14+
15+
commentSchema.plugin(uniqueValidator);
16+
17+
commentSchema.set("toJSON", {
18+
transform: (document, returnedObject) => {
19+
returnedObject.id = returnedObject._id.toString();
20+
delete returnedObject._id;
21+
delete returnedObject.__v;
22+
},
23+
});
24+
25+
module.exports = mongoose.model("Comment", commentSchema);

part7/extended-blog/models/user.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const mongoose = require("mongoose");
2+
const uniqueValidator = require("mongoose-unique-validator");
3+
4+
const userSchema = new mongoose.Schema({
5+
username: { type: String, unique: true },
6+
passwordHash: String,
7+
name: { type: String, minLength: 4 },
8+
blogs: [
9+
{
10+
type: mongoose.Schema.Types.ObjectId,
11+
ref: "Blog",
12+
},
13+
],
14+
});
15+
16+
userSchema.plugin(uniqueValidator, {
17+
message: "Error, {PATH} already exist.",
18+
});
19+
userSchema.set("toJSON", {
20+
transform: (document, returnedObject) => {
21+
returnedObject.id = returnedObject._id.toString();
22+
delete returnedObject._id;
23+
delete returnedObject.__v;
24+
// the passwordHash should not be revealed
25+
delete returnedObject.passwordHash;
26+
},
27+
});
28+
29+
module.exports = mongoose.model("User", userSchema);

0 commit comments

Comments
 (0)