Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 29 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
const express = require('express')
const handlebars = require('express-handlebars')
const path = require('path')
const session = require('express-session')
const flash = require('connect-flash')
const methodOverride = require('method-override')

const passport = require('./config/passport')
const routes = require('./routes')
const handlebarsHelper = require('./helpers/handlerBars-helper')
const { getUser } = require('./helpers/auth-helper')

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

app.engine('hbs', handlebars({ extname: '.hbs', helpers: handlebarsHelper }))
app.set('view engine', 'hbs')
app.use(express.urlencoded({ extended: true }))
app.use(session({
secret: SESSION_SECRET,
resave: false,
saveUninitialized: false
}))
app.use(flash())
app.use(passport.initialize())
app.use(passport.session())
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(methodOverride('_method'))
app.use('/upload', express.static(path.join(__dirname, 'upload')))
app.use(routes)

app.listen(port, () => {
Expand Down
32 changes: 32 additions & 0 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const passport = require('passport')
const LocalStrategy = require('passport-local')
const bcrypt = require('bcryptjs')
const User = require('../models').User

passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
(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(isMatch => {
if (!isMatch) return cb(null, false, req.flash('error_messages', '帳號或密碼輸入錯誤!'))
return cb(null, 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
109 changes: 109 additions & 0 deletions controllers/admin-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const { Restaurant, User } = require('../models')
const { localFileHandler } = require('../helpers/file-helper')

module.exports = {
async getRestaurants (_req, res, next) {
try {
const restaurants = await Restaurant.findAll({ raw: true })

res.render('admin/restaurants', { restaurants, currentPage: 'restaurants' })
} catch (err) {
next(err)
}
},
async createRestaurant (_req, res) {
res.render('admin/create-restaurant')
},
async postRestaurant (req, res, next) {
try {
const { name, tel, address, openingHours, description } = req.body

if (!name || !name.replace(/\s/g, '').length) throw new Error('Restaurant name is required')

const filePath = await localFileHandler(req.file)
await Restaurant.create({ name, tel, address, openingHours, description, image: filePath || null })
req.flash('success_messages', 'A restaurant was successfully created')
res.redirect('/admin/restaurants')
} catch (err) {
next(err)
}
},
async getRestaurant (req, res, next) {
try {
const restaurant = await Restaurant.findByPk(req.params.id, { raw: true })

if (!restaurant) throw new Error('The restaurant is not existed.')
res.render('admin/restaurant', { restaurant })
} catch (err) {
next(err)
}
},
async editRestaurant (req, res, next) {
try {
const restaurant = await Restaurant.findByPk(req.params.id, { raw: true })

if (!restaurant) throw new Error('The restaurant is not existed.')
res.render('admin/edit-restaurant', { restaurant })
} catch (err) {
next(err)
}
},
async putRestaurant (req, res, next) {
try {
const { name, tel, address, openingHours, description } = req.body

if (!name || !name.replace(/\s/g, '').length) throw new Error('Restaurant name is required')

const [restaurant, filePath] = await Promise.all([
Restaurant.findByPk(req.params.id),
localFileHandler(req.file)
])

if (!restaurant) throw new Error('The restaurant is not existed')
await restaurant.update({ name, tel, address, openingHours, description, image: filePath || restaurant.image })
req.flash('success_messages', 'The restaurant was successfully to update')
res.redirect('/admin/restaurants')
} catch (err) {
next(err)
}
},
async deleteRestaurant (req, res, next) {
try {
const restaurant = await Restaurant.findByPk(req.params.id)

if (!restaurant) throw new Error('The restaurant is not existed')
await restaurant.destroy()
res.redirect('/admin/restaurants')
} catch (err) {
next(err)
}
},
async getUsers (_req, res, next) {
try {
const users = await User.findAll({ raw: true })

res.render('admin/users', { users, currentPage: 'users' })
} catch (err) {
next(err)
}
},
async patchUser (req, res, next) {
try {
const userId = req.params.id
if (!userId) throw new Error('User id is required')

const user = await User.findByPk(userId)
if (!user) throw new Error('The user is not existed')
if (user.email === 'root@example.com') {
req.flash('error_messages', '禁止變更 root 權限')
res.redirect('back')
} else {
await user.update({ isAdmin: !user.isAdmin })
req.flash('success_messages', '使用者權限變更成功')
res.redirect('/admin/users')
}
} catch (err) {
next(err)
}
}
}
7 changes: 7 additions & 0 deletions controllers/restaurant-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const restaurantController = {
getRestaurants (_req, res) {
return res.render('restaurants')
}
}

module.exports = restaurantController
43 changes: 43 additions & 0 deletions controllers/user-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const bcrypt = require('bcryptjs')
const User = require('../models').User

const userController = {
getSignUpPage (_req, res) {
res.render('signup')
},
signUp (req, res, next) {
const { name, email, password, passwordCheck } = req.body

if (password !== passwordCheck) throw new Error('Password do not match')
User.findOne({ where: { email } })
.then(user => {
if (user) throw new Error('Email is already used')
return bcrypt.hash(req.body.password, 10)
})
.then(hash => User.create({ name, email, password: hash }))
.then(() => {
req.flash('success_messages', '成功註冊帳號')
res.render('signin')
})
.catch(err => next(err))
},
getSignInPage (_req, res) {
res.render('signin')
},
signin (req, res) {
req.flash('success_messages', '登入成功')
res.redirect('/restaurants')
},
logout (req, res, next) {
req.flash('success_messages', '登出成功')
req.logout(err => {
if (err) {
err.alertMsg = '登出失敗'
return next(err)
}
res.redirect('/signin')
})
}
}

module.exports = userController
8 changes: 8 additions & 0 deletions helpers/auth-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
getUser (req) {
return req.user || null
},
ensureAuthenticated (req) {
return req.isAuthenticated()
}
}
15 changes: 15 additions & 0 deletions helpers/file-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const fs = require('fs/promises')

module.exports = {
localFileHandler (file) {
return new Promise((resolve, reject) => {
if (!file) return resolve(null)

const fileName = `upload/${file.originalname}`
return fs.readFile(file.path)
.then(data => fs.writeFile(fileName, data))
.then(() => resolve(`/${fileName}`))
.catch(err => reject(err))
})
}
}
9 changes: 9 additions & 0 deletions helpers/handlerBars-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const dayjs = require('dayjs')

module.exports = {
currentYear: () => dayjs().year(),
activeRestaurantsTab: currentPage => currentPage === 'restaurants' ? 'active' : '',
activeUsersTab: currentPage => currentPage === 'users' ? 'active' : '',
userRole: isAdmin => isAdmin ? 'admin' : 'user',
becomeRole: isAdmin => isAdmin ? 'set as user' : 'set as admin'
}
Loading