Skip to content

Commit f731669

Browse files
committed
part7 all done
1 parent 27613ae commit f731669

Some content is hidden

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

92 files changed

+70974
-0
lines changed

part7/bloglist-backend/.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
MONGODB_URI='mongodb+srv://fullstack:guwahati@cluster0-wsqxa.mongodb.net/blog-app?retryWrites=true&w=majority'
2+
TEST_MONGODB_URI='mongodb+srv://fullstack:guwahati@cluster0-wsqxa.mongodb.net/blog-app-test?retryWrites=true&w=majority'
3+
PORT=3001
4+
SECRET=imlazy

part7/bloglist-backend/.eslintrc.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// /blogs-backend/_eslintrc.js
2+
module.exports = {
3+
'env': {
4+
'commonjs': true,
5+
'es6': true,
6+
'node': true,
7+
"jest": true
8+
},
9+
'extends': 'eslint:recommended',
10+
'globals': {
11+
'Atomics': 'readonly',
12+
'SharedArrayBuffer': 'readonly'
13+
},
14+
'parserOptions': {
15+
'ecmaVersion': 2018
16+
},
17+
'rules': {
18+
'indent': [
19+
'error',
20+
2
21+
],
22+
'quotes': [
23+
'error',
24+
'single'
25+
],
26+
'semi': [
27+
'error',
28+
'never'
29+
],
30+
'eqeqeq': 'error',
31+
'no-trailing-spaces': 'error',
32+
'object-curly-spacing': [
33+
'error', 'always'
34+
],
35+
'arrow-spacing': [
36+
'error', { 'before': true, 'after': true }
37+
],
38+
'no-console': 0
39+
}
40+
}

part7/bloglist-backend/app.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// /blogs-backend/app.js
2+
const config = require('./utils/config')
3+
const express = require('express')
4+
require('express-async-errors')
5+
const cors = require('cors')
6+
const logger = require('./utils/logger')
7+
const middleware = require('./utils/middleware')
8+
const blogsRouter = require('./controllers/blogs')
9+
const usersRouter = require('./controllers/users')
10+
const loginRouter = require('./controllers/login')
11+
const mongoose = require('mongoose')
12+
13+
const app = express()
14+
15+
mongoose.set('useCreateIndex', true)
16+
17+
mongoose.connect(config.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true })
18+
.then(() => {
19+
logger.info('connected to MongoDB')
20+
})
21+
.catch((error) => {
22+
logger.error('error connection to MongoDB:', error.message)
23+
})
24+
mongoose.set('useFindAndModify', false)
25+
26+
app.use(cors())
27+
app.use(express.json())
28+
app.use(middleware.requestLogger)
29+
app.use(middleware.tokenExtractor)
30+
31+
app.use('/api/blogs', blogsRouter)
32+
app.use('/api/users', usersRouter)
33+
app.use('/api/login', loginRouter)
34+
35+
if(process.env.NODE_ENV === 'test') {
36+
const testingRouter = require('./controllers/testing')
37+
app.use('/api/testing', testingRouter)
38+
}
39+
40+
app.use(middleware.errorHandler)
41+
42+
module.exports = app
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// /blogs-backend/controllers/blogs.js
2+
const router = require('express').Router()
3+
const jwt = require('jsonwebtoken')
4+
const Blog = require('../models/blog')
5+
const User = require('../models/user')
6+
const Comment = require('../models/comment')
7+
8+
router.get('/', async (request, response) => {
9+
const blogs = await Blog
10+
.find({})
11+
.populate('user', { username: 1, name: 1 })
12+
.populate('comment')
13+
14+
response.json(blogs)
15+
})
16+
17+
router.delete('/:id', async (request, response) => {
18+
const decodedToken = jwt.verify(request.token, process.env.SECRET)
19+
20+
if (!request.token || !decodedToken.id) {
21+
return response.status(401).json({ error: 'token missing or invalid' })
22+
}
23+
24+
const user = await User.findById(decodedToken.id)
25+
const blog = await Blog.findById(request.params.id)
26+
if (blog.user.toString() !== user.id.toString()) {
27+
return response.status(401).json({ error: 'only the creator can delete blogs' })
28+
}
29+
30+
await blog.remove()
31+
user.blogs = user.blogs.filter(b => b.id.toString() !== request.params.id.toString())
32+
await user.save()
33+
response.status(204).end()
34+
})
35+
36+
router.put('/:id', async (request, response) => {
37+
const blog = request.body
38+
39+
const updatedBlog = await Blog
40+
.findByIdAndUpdate(request.params.id, blog, { new: true })
41+
.populate('user', { username: 1, name: 1 })
42+
response.json(updatedBlog.toJSON())
43+
})
44+
45+
// fix it : also populate in user the comment
46+
router.post('/:id/comments', async (request, response) => {
47+
const comment = new Comment(request.body)
48+
const blog = await Blog.findById(request.params.id)
49+
50+
// later you'll have to use an update query
51+
const savedComment = await comment.save()
52+
blog.comment = blog.comment.concat(savedComment._id)
53+
await blog.save()
54+
55+
response.json(savedComment)
56+
} )
57+
58+
router.post('/', async (request, response) => {
59+
const blog = new Blog(request.body)
60+
61+
const decodedToken = jwt.verify(request.token, process.env.SECRET)
62+
63+
if (!request.token || !decodedToken.id) {
64+
return response.status(401).json({ error: 'token missing or invalid' })
65+
}
66+
67+
const user = await User.findById(decodedToken.id)
68+
69+
if (!blog.url || !blog.title) {
70+
return response.status(400).send({ error: 'title or url missing ' })
71+
}
72+
73+
if (!blog.likes) {
74+
blog.likes = 0
75+
}
76+
77+
blog.user = user
78+
const savedBlog = await blog.save()
79+
80+
user.blogs = user.blogs.concat(savedBlog._id)
81+
await user.save()
82+
83+
response.status(201).json(savedBlog)
84+
})
85+
86+
module.exports = router
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// /blogs-backend/controllers/login.js
2+
const jwt = require('jsonwebtoken')
3+
const bcrypt = require('bcrypt')
4+
const router = require('express').Router()
5+
const User = require('../models/user')
6+
7+
router.post('/', async (request, response) => {
8+
const body = request.body
9+
10+
const user = await User.findOne({ username: body.username })
11+
12+
const passwordCorrect = user === null
13+
? false
14+
: await bcrypt.compare(body.password, user.passwordHash)
15+
16+
if (!(user && passwordCorrect)) {
17+
return response.status(401).json({
18+
error: 'invalid username or password'
19+
})
20+
}
21+
22+
const userForToken = {
23+
username: user.username,
24+
id: user._id,
25+
}
26+
27+
const token = jwt.sign(userForToken, process.env.SECRET)
28+
29+
response
30+
.status(200)
31+
.send({ token, username: user.username, name: user.name })
32+
})
33+
34+
module.exports = router
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// define route to clear the db state
2+
3+
const router = require('express').Router()
4+
const Blog = require('../models/blog')
5+
const User = require('../models/user')
6+
7+
router.post('/reset', async (request, response) => {
8+
await Blog.deleteMany({})
9+
await User.deleteMany({})
10+
11+
response.status(204).end()
12+
})
13+
14+
module.exports = router
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// /blogs-backend/controllers/users.js
2+
const bcrypt = require('bcrypt')
3+
const router = require('express').Router()
4+
const User = require('../models/user')
5+
6+
router.get('/', async (request, response) => {
7+
const users = await User
8+
.find({})
9+
.populate('blogs', { title: 1, url: 1, likes: 1, author: 1, comment: 1 })
10+
// .populate('blogs.comment')
11+
12+
response.json(users.map(u => u.toJSON()))
13+
})
14+
15+
router.post('/', async (request, response) => {
16+
const { password, name, username } = request.body
17+
18+
if ( !password || password.length<3 ) {
19+
return response.status(400).send({
20+
error: 'password must min length 3'
21+
})
22+
}
23+
24+
const saltRounds = 10
25+
const passwordHash = await bcrypt.hash(password, saltRounds)
26+
27+
const user = new User({
28+
username, name,
29+
passwordHash,
30+
})
31+
32+
const savedUser = await user.save()
33+
34+
response.json(savedUser)
35+
})
36+
37+
module.exports = router

part7/bloglist-backend/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// /blogs-backend/index.js
2+
const app = require('./app')
3+
const http = require('http')
4+
const config = require('./utils/config')
5+
const logger = require('./utils/logger')
6+
7+
const server = http.createServer(app)
8+
9+
server.listen(config.PORT, () => {
10+
logger.info(`Server running on port ${config.PORT}`)
11+
})

part7/bloglist-backend/jest.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// /blogs-backend/jest.config.js
2+
module.exports = {
3+
testEnvironment: 'node'
4+
}

part7/bloglist-backend/models/blog.js

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

part7/bloglist-backend/models/user.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// /blogs-backend/models/user.js
2+
const mongoose = require('mongoose')
3+
const uniqueValidator = require('mongoose-unique-validator')
4+
5+
const userSchema = mongoose.Schema({
6+
username: {
7+
type: String,
8+
minlength: 3,
9+
unique: true
10+
},
11+
name: String,
12+
passwordHash: String,
13+
blogs: [
14+
{
15+
type: mongoose.Schema.Types.ObjectId,
16+
ref: 'Blog'
17+
}
18+
],
19+
})
20+
21+
userSchema.set('toJSON', {
22+
transform: (document, returnedObject) => {
23+
returnedObject.id = returnedObject._id.toString()
24+
delete returnedObject._id
25+
delete returnedObject.__v
26+
delete returnedObject.passwordHash
27+
}
28+
})
29+
30+
userSchema.plugin(uniqueValidator)
31+
32+
const User = mongoose.model('User', userSchema)
33+
34+
module.exports = User

0 commit comments

Comments
 (0)