Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7ece79e
add R01.test.js && unit-test-helper.js
AmberYen Mar 2, 2022
79ff158
註解 typo 修正
zjzheng17 Jul 18, 2022
03c14c0
correcting typo in comment
zjzheng17 Jul 18, 2022
d5b1553
chore: init github workflow
eugenechen0514 Sep 9, 2023
fe01b7f
Update unit-test-helper.js
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
fa173a7
chore: fix code format
eugenechen0514 Sep 17, 2023
1b8cd24
Delete .travis.yml
tuterwell Sep 18, 2023
ecba2d3
Chapter4 M4 section
PeiTzuChen Jan 14, 2024
9ba12aa
fix conflict
PeiTzuChen Jan 14, 2024
26bb134
R01 user authority
PeiTzuChen Jan 14, 2024
1695909
fix bug
PeiTzuChen Jan 14, 2024
3128ce9
R01 user authority
PeiTzuChen Jan 14, 2024
493eb5a
Merge commit '1695909ba6db0ccd4fd550cb417df3380a3e6d46' into R01
PeiTzuChen Jan 14, 2024
6582aba
feat: add category model
PeiTzuChen Jan 16, 2024
35f011f
feat: update seed files
PeiTzuChen Jan 16, 2024
ecf8d7b
feat: show category on admin restaurant pages
PeiTzuChen Jan 16, 2024
c0819f4
feat: add categories selector on admin create and edit page
PeiTzuChen Jan 17, 2024
2dc281c
feat: add ifCond hbs helper & update category selector
PeiTzuChen Jan 17, 2024
7705ec2
feat: add admin categories page
PeiTzuChen Jan 17, 2024
9b78691
feat: category create
PeiTzuChen Jan 17, 2024
ed2a83b
feat: category update
PeiTzuChen Jan 17, 2024
dfe856c
feat: category delete
PeiTzuChen Jan 17, 2024
27b5be5
feat: add restaurants index page
PeiTzuChen Jan 17, 2024
1cb3f94
feat: add restaurant page
PeiTzuChen Jan 17, 2024
918f434
use ternary operator to optimize coding
PeiTzuChen Jan 18, 2024
e11d678
R02 finished
PeiTzuChen Jan 19, 2024
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
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/node_modules/*
/tests/*
/tests/*
18 changes: 5 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
name: Node.js test

on:
pull_request_target:
push:
branches:
- main
# push:
# branches:
# - main
# - 'R*'
# - '*-test'
# pull_request:
# branches:
- '*-test'
pull_request:
branches:
# - main

env:
Expand Down Expand Up @@ -38,11 +34,7 @@ jobs:
# mysql user: 'github' # Required if "mysql root password" is empty, default is empty. The superuser for the specified database. Can use secrets, too
# mysql password: 'password' # Required if "mysql user" exists. The password for the "mysql user"
- run: mysql --version
- run: echo "checkout head ${{ github.event.pull_request.head.sha }}"
- run: echo "base ${{ github.event.pull_request.base.sha }}"
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
Expand Down
34 changes: 32 additions & 2 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
const express = require('express')
const routes = require('./routes')

const handlebars = require('express-handlebars')
const app = express()
const port = process.env.PORT || 3000
const flash = require('connect-flash')
const session = require('express-session')
const SESSION_SECRET = 'secret'
const passport = require('./config/passport')
const { getUser } = require('./helpers/auth-helpers')
const handlebarsHelpers = require('./helpers/hbs-helpers')
const methodOverride = require('method-override')

app.engine('.hbs', handlebars({ extname: '.hbs', helpers: handlebarsHelpers }))
app.set('view engine', '.hbs')
app.set('views', './views')

app.use(express.urlencoded({ extended: true }))
app.use(
session({
secret: SESSION_SECRET,
resave: false,
saveUninitialized: false
})
)
app.use(passport.initialize())
app.use(passport.session())
app.use(flash())
app.use(methodOverride('_method'))
app.use('/upload', express.static('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)

app.listen(port, () => {
console.info(`Example app listening on port ${port}!`)
console.info(`Example app listening on http://localhost:${port}`)
})

module.exports = app
51 changes: 51 additions & 0 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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)
})
}).catch(error => {
req.flash('error_messages', '登入失敗')
return cb(error)
})
}
)
)
// serialize and deserialize user
passport.serializeUser((user, cb) => {
cb(null, user.id)
})
passport.deserializeUser((id, cb) => {
User.findByPk(id, { raw: true }).then(user => {
cb(null, user)
})
})
module.exports = passport
142 changes: 142 additions & 0 deletions controllers/admin-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
const { Restaurant, User, Category } = require('../models')
const { localFileHandler } = require('../helpers/file-helpers')

const adminController = {
getRestaurants: (req, res, next) => {
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 // 從 req.body 拿出表單裡的資料
if (!name) throw new Error('Restaurant name is required!') // name 是必填,若發先是空值就會終止程式碼,並在畫面顯示錯誤提示
const { file } = req
localFileHandler(file)
.then(filePath =>
Restaurant.create({
// 產生一個新的 Restaurant 物件實例,並存入資料庫
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) => {
Restaurant.findByPk(req.params.id, {
// 去資料庫用 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 didn'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
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
})
.then(users => {
return 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("The user doesn't exist!")
if (user.dataValues.email === 'root@example.com') {
req.flash('error_messages', '禁止變更 root 權限')
return res.redirect('back')
}
user.dataValues.isAdmin
? user.update({
isAdmin: false
})
: user.update({
isAdmin: true
})
})
.then(() => {
req.flash('success_messages', '使用者權限變更成功')
return 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
50 changes: 50 additions & 0 deletions controllers/restaurant-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const { Restaurant, Category } = require('../models')
const restaurantController = {
getRestaurants: (req, res, next) => {
return Restaurant.findAll({
include: Category,
nest: true,
raw: true
})
.then(restaurants => {
const data = restaurants.map(r => ({
...r,
description: r.description.substring(0, 50)
}))
return res.render('restaurants', {
restaurants: data
})
})
.catch(error => next(error))
},
getRestaurant: (req, res, next) => {
return Restaurant.findByPk(req.params.id, {
include: Category,
nest: true
})
.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,
nest: true,
raw: true
})
.then(restaurant => {
if (!restaurant) throw new Error("Restaurant didn't exist!")
return res.render('dashboard', { restaurant })
})
.catch(err => next(err))
}

}
module.exports = restaurantController
Loading