Skip to content
Open

R03 #23

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
fca7f3b
feat: add R01.test.js and unit-test-helpers.js
Carrot7712 Jan 13, 2022
96da4be
feat:add R02.test.js
Carrot7712 Jan 13, 2022
7ece79e
add R01.test.js && unit-test-helper.js
AmberYen Mar 2, 2022
b54a2e9
add R02.test.js file
AmberYen Mar 2, 2022
ef44c3e
add R03.test.js file
AmberYen Mar 2, 2022
79ff158
註解 typo 修正
zjzheng17 Jul 18, 2022
9d566b6
correcting typo in comment
zjzheng17 Jul 18, 2022
4fe60b0
correcting typo in comment
zjzheng17 Jul 18, 2022
03c14c0
correcting typo in comment
zjzheng17 Jul 18, 2022
d5b1553
chore: init github workflow
eugenechen0514 Sep 9, 2023
6f29ed3
chore: init github workflow
eugenechen0514 Sep 9, 2023
02a746f
chore: init github workflow
eugenechen0514 Sep 9, 2023
fe01b7f
Update unit-test-helper.js
tuterwell Sep 12, 2023
13c29eb
Update unit-test-helper.js update()
tuterwell Sep 12, 2023
e2665da
chore: format code
eugenechen0514 Sep 17, 2023
1be932b
test: fix update method
eugenechen0514 Sep 17, 2023
d12a942
Merge commit '1be932b9d1998168679d2cdf5a7a9e2f1d2bca91' into R01-test
eugenechen0514 Sep 17, 2023
20ab723
Merge branch 'R01-test' into R02-test
eugenechen0514 Sep 17, 2023
fa173a7
chore: fix code format
eugenechen0514 Sep 17, 2023
12487dc
Merge branch 'R02-test' into R03-test
eugenechen0514 Sep 17, 2023
1b8cd24
Delete .travis.yml
tuterwell Sep 18, 2023
a449027
Delete .travis.yml
tuterwell Sep 18, 2023
0274241
feat: add handlebars
shccgxqp Oct 1, 2023
919638b
feat: add index page
shccgxqp Oct 1, 2023
b264746
feat: add admin index page
shccgxqp Oct 1, 2023
a0c42c8
feat: add user model
shccgxqp Oct 1, 2023
29a03e2
feat: user signup
shccgxqp Oct 1, 2023
1627a60
feat: flash msg & signup verification
shccgxqp Oct 1, 2023
331a0f8
feat: passport init & signin
shccgxqp Oct 1, 2023
84c9721
feat: add header & footer
shccgxqp Oct 1, 2023
7c89c87
feat: user model add is_admin
shccgxqp Oct 2, 2023
99112f5
feat: add restaurant model
shccgxqp Oct 2, 2023
1e4e1b9
feat: modify admin restaurants page
shccgxqp Oct 2, 2023
45f44f5
feat: create restaurant
shccgxqp Oct 2, 2023
262afac
feat: admin restaurant page
shccgxqp Oct 2, 2023
3c99221
feat: admin update restaurant
shccgxqp Oct 2, 2023
7a6bef5
feat: delete restaurant
shccgxqp Oct 2, 2023
94634c4
feat: add restaurant image
shccgxqp Oct 2, 2023
ac4307f
feat: add seed data
shccgxqp Oct 2, 2023
f8d54dc
Merge remote-tracking branch 'upstream/R01-test' into R01
shccgxqp Oct 2, 2023
b7775ff
feat:adjust middleware/auth.js
shccgxqp Oct 2, 2023
2112ddc
feat: A16 R01 add users feature
shccgxqp Oct 2, 2023
417316b
feat: add category model
shccgxqp Oct 2, 2023
5a32725
feat: update seed files
shccgxqp Oct 2, 2023
36117a6
feat: show category on admin restaurant pages
shccgxqp Oct 3, 2023
e146121
feat: add categories selector on admin create and edit page
shccgxqp Oct 3, 2023
5328365
feat: add ifCond hbs helper & update category selector
shccgxqp Oct 3, 2023
15bce9b
feat: add admin categories page
shccgxqp Oct 3, 2023
b65bb19
feat: category create
shccgxqp Oct 3, 2023
37acbc6
feat: category update
shccgxqp Oct 3, 2023
11fcef1
feat: category delete
shccgxqp Oct 3, 2023
f50ed6e
feat: add restaurants index page
shccgxqp Oct 3, 2023
edef6c6
feat: add restaurant page
shccgxqp Oct 3, 2023
6f50dc4
feat: add dashboard
shccgxqp Oct 3, 2023
7d4c74d
feat: add categories navbar on restaurants index page
shccgxqp Oct 3, 2023
4d9ef9a
feat: add pagination on restaurants index page
shccgxqp Oct 3, 2023
567038c
feat: add comment model
shccgxqp Oct 3, 2023
81f97d5
feat: add post comment on restaurant page
shccgxqp Oct 3, 2023
19b531e
feat: show comments on restaurant page
shccgxqp Oct 4, 2023
1486027
feat: add delete comment for admin
shccgxqp Oct 4, 2023
bfc5624
feat:add user profile page and edit profile
shccgxqp Oct 4, 2023
d12b0f0
Merge remote-tracking branch 'upstream/R03-test' into R03
shccgxqp Oct 4, 2023
3604fea
feat: comments on user/profile
shccgxqp Oct 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"workbench.colorCustomizations": {
"terminal.background": "#131513",
"terminal.foreground": "#8CA68C",
"terminalCursor.background": "#8CA68C",
"terminalCursor.foreground": "#8CA68C",
"terminal.ansiBlack": "#131513",
"terminal.ansiBlue": "#3D62F5",
"terminal.ansiBrightBlack": "#687D68",
"terminal.ansiBrightBlue": "#3D62F5",
"terminal.ansiBrightCyan": "#1999B3",
"terminal.ansiBrightGreen": "#29A329",
"terminal.ansiBrightMagenta": "#AD2BEE",
"terminal.ansiBrightRed": "#E6193C",
"terminal.ansiBrightWhite": "#F4FBF4",
"terminal.ansiBrightYellow": "#98981B",
"terminal.ansiCyan": "#1999B3",
"terminal.ansiGreen": "#29A329",
"terminal.ansiMagenta": "#AD2BEE",
"terminal.ansiRed": "#E6193C",
"terminal.ansiWhite": "#8CA68C",
"terminal.ansiYellow": "#98981B"
}
}
30 changes: 30 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
const path = require('path')
const express = require('express')
const handlebars = require('express-handlebars') // 引入 express-handlebars
const flash = require('connect-flash')
const methodOverride = require('method-override')
const session = require('express-session')
const passport = require('./config/passport') // 引入 passport
const handlebarsHelpers = require('./helpers/handlebars-helpers')
const routes = require('./routes')
const { getUser } = require('./helpers/auth-helpers')

const app = express()
const port = process.env.PORT || 3000
const SESSION_SECRET = 'secret'

app.engine('hbs', handlebars({ extname: '.hbs', helpers: handlebarsHelpers }))
app.set('view engine', 'hbs')
app.use(express.urlencoded({ extended: true }))
app.use(session({
secret: SESSION_SECRET,
resave: true,
saveUninitialized: true
}))
app.use(passport.initialize()) // 增加這行,初始化 Passport
app.use(passport.session()) // 增加這行,啟動 session 功能
app.use(flash())
app.use(methodOverride('_method'))
app.use('/upload', express.static(path.join(__dirname, 'upload')))

app.use((req, res, next) => {
res.locals.success_messages = req.flash('success_messages')
res.locals.error_messages = req.flash('error_messages')
res.locals.user = getUser(req)
next()
})

app.use(routes)

Expand Down
36 changes: 36 additions & 0 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const passport = require('passport')
const LocalStrategy = require('passport-local')
const bcrypt = require('bcryptjs')
const db = require('../models')
const User = db.User
// set up Passport strategy
passport.use(new LocalStrategy(
// customize user field
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
// authenticate user
(req, email, password, cb) => {
User.findOne({ where: { email } })
.then(user => {
if (!user) return cb(null, false, req.flash('error_messages', '帳號或密碼輸入錯誤!'))
bcrypt.compare(password, user.password).then(res => {
if (!res) return cb(null, false, req.flash('error_messages', '帳號或密碼輸入錯誤!'))
return cb(null, user)
})
})
}
))
// serialize and deserialize user
passport.serializeUser((user, cb) => {
cb(null, user.id)
})
passport.deserializeUser((id, cb) => {
User.findByPk(id).then(user => {
user = user.toJSON()
return cb(null, user)
})
})
module.exports = passport
135 changes: 135 additions & 0 deletions controllers/admin-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
const { Restaurant, User, Category } = require('../models')
const { localFileHandler } = require('../helpers/file-helpers')

const adminController = {
getRestaurants: (req, res, next) => {
return Restaurant.findAll({
raw: true,
nest: true,
include: [Category]
})
.then(restaurants => res.render('admin/restaurants', { restaurants }))
.catch(err => next(err))
},
createRestaurant: (req, res, next) => {
return Category.findAll({
raw: true
})
.then(categories => res.render('admin/create-restaurant', { categories }))
.catch(err => next(err))
},
postRestaurant: (req, res, next) => {
const { name, tel, address, openingHours, description, categoryId } = req.body
if (!name) throw new Error('Restaurant name is required!')

const { file } = req

return localFileHandler(file)
.then(filePath => Restaurant.create({
name,
tel,
address,
openingHours,
description,
image: filePath || null,
categoryId
}))
.then(() => {
req.flash('success_messages', 'restaurant was successfully created')
res.redirect('/admin/restaurants')
})
.catch(err => next(err))
},
getRestaurant: (req, res, next) => {
return Restaurant.findByPk(req.params.id, {
raw: true,
nest: true,
include: [Category]
})
.then(restaurant => {
if (!restaurant) throw new Error("Restaurant didn't exist!")

res.render('admin/restaurant', { restaurant })
})
.catch(err => next(err))
},
editRestaurant: (req, res, next) => {
return Promise.all([
Restaurant.findByPk(req.params.id, { raw: true }),
Category.findAll({ raw: true })
])
.then(([restaurant, categories]) => {
if (!restaurant) throw new Error("Restaurant doesn't exist!")

res.render('admin/edit-restaurant', { restaurant, categories })
})
.catch(err => next(err))
},
putRestaurant: (req, res, next) => {
const { name, tel, address, openingHours, description, categoryId } = req.body
if (!name) throw new Error('Restaurant name is required!')

const { file } = req

return Promise.all([
Restaurant.findByPk(req.params.id),
localFileHandler(file)
])
.then(([restaurant, filePath]) => {
if (!restaurant) throw new Error("Restaurant didn't exist!")

return restaurant.update({
name,
tel,
address,
openingHours,
description,
image: filePath || restaurant.image,
categoryId
})
})
.then(() => {
req.flash('success_messages', 'restaurant was successfully to update')
res.redirect('/admin/restaurants')
})
.catch(err => next(err))
},
deleteRestaurant: (req, res, next) => {
return Restaurant.findByPk(req.params.id)
.then(restaurant => {
if (!restaurant) throw new Error("Restaurant didn't exist!")

return restaurant.destroy()
})
.then(() => res.redirect('/admin/restaurants'))
.catch(err => next(err))
},
getUsers: (req, res, next) => {
return User.findAll({
raw: true,
nest: true
})
.then(users => res.render('admin/users', { users }))
.catch(err => next(err))
},
patchUser: (req, res, next) => {
return User.findByPk(req.params.id)
.then(user => {
if (!user) throw new Error("User didn't exist!")
if (user.email === 'root@example.com') {
req.flash('error_messages', '禁止變更 root 權限')
return res.redirect('back')
}

return user.update({ isAdmin: !user.isAdmin })
})
.then(() => {
req.flash('success_messages', '使用者權限變更成功')
res.redirect('/admin/users')
})
.catch(err => next(err))
}

}

module.exports = adminController
45 changes: 45 additions & 0 deletions controllers/category-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const { Category } = require('../models')
const categoryController = {
getCategories: (req, res, next) => {
// 修改以下
return Promise.all([
Category.findAll({ raw: true }),
req.params.id ? Category.findByPk(req.params.id, { raw: true }) : null
])
.then(([categories, category]) => {
res.render('admin/categories', {
categories,
category
})
})
.catch(err => next(err))
},
postCategory: (req, res, next) => {
const { name } = req.body
if (!name) throw new Error('Category name is required!')
return Category.create({ name })
.then(() => res.redirect('/admin/categories'))
.catch(err => next(err))
},
putCategory: (req, res, next) => {
const { name } = req.body
if (!name) throw new Error('Category name is required!')
return Category.findByPk(req.params.id)
.then(category => {
if (!category) throw new Error("Category doesn't exist!")
return category.update({ name })
})
.then(() => res.redirect('/admin/categories'))
.catch(err => next(err))
},
deleteCategory: (req, res, next) => {
return Category.findByPk(req.params.id)
.then(category => {
if (!category) throw new Error("Category didn't exist!") // 反查,確認要刪除的類別存在,再進行下面刪除動作
return category.destroy()
})
.then(() => res.redirect('/admin/categories'))
.catch(err => next(err))
}
}
module.exports = categoryController
36 changes: 36 additions & 0 deletions controllers/comment-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const { Comment, User, Restaurant } = require('../models')

const commentController = {
postComment: (req, res, next) => {
const { restaurantId, text } = req.body
const userId = req.user.id
if (!text) throw new Error('Comment text is required!')
return Promise.all([
User.findByPk(userId),
Restaurant.findByPk(restaurantId)
])
.then(([user, restaurant]) => {
if (!user) throw new Error("User didn't exist!")
if (!restaurant) throw new Error("Restaurant didn't exist!")
return Comment.create({
text,
restaurantId,
userId
})
})
.then(() => {
res.redirect(`/restaurants/${restaurantId}`)
})
.catch(err => next(err))
}, // 新增以下
deleteComment: (req, res, next) => {
return Comment.findByPk(req.params.id)
.then(comment => {
if (!comment) throw new Error("Comment didn't exist!")
return comment.destroy()
})
.then(deletedComment => res.redirect(`/restaurants/${deletedComment.restaurantId}`))
.catch(err => next(err))
}
}
module.exports = commentController
66 changes: 66 additions & 0 deletions controllers/restaurant-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const { Restaurant, Category, Comment, User } = require('../models')
const { getOffset, getPagination } = require('../helpers/pagination-helper')
const restaurantController = {
getRestaurants: (req, res) => {
const DEFAULT_LIMIT = 9
const categoryId = Number(req.query.categoryId) || ''
const page = Number(req.query.page) || 1
const limit = Number(req.query.limit) || DEFAULT_LIMIT
const offset = getOffset(limit, page)

return Promise.all([
Restaurant.findAndCountAll({
include: Category,
where: {
...categoryId ? { categoryId } : {}
},
limit,
offset,
nest: true,
raw: true
}),
Category.findAll({ raw: true })
])
.then(([restaurants, categories]) => {
const data = restaurants.rows.map(r => ({ // 修改這裡,加上 .rows
...r,
description: r.description.substring(0, 50)
}))
return res.render('restaurants', {
restaurants: data,
categories,
categoryId,
pagination: getPagination(limit, page, restaurants.count) // 修改這裡,把 pagination 資料傳回樣板
})
})
}, // 加逗號,新增以下
getRestaurant: (req, res, next) => {
return Restaurant.findByPk(req.params.id, {
include: [
Category,
{ model: Comment, include: User }
]
})
.then(restaurant => {
if (!restaurant) throw new Error("Restaurant didn't exist!")
return restaurant.increment('viewCounts')
})
.then(restaurant => {
res.render('restaurant', { restaurant: restaurant.toJSON() })
})
.catch(err => next(err))
},
getDashboard: (req, res, next) => {
return Restaurant.findByPk(req.params.id, {
include: Category,
raw: true,
nest: true
})
.then(restaurant => {
if (!restaurant) throw new Error("Dashboard didn't exist!")
res.render('dashboard', { restaurant })
})
.catch(err => next(err))
}
}
module.exports = restaurantController
Loading