Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 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
37322b4
feat:add R03.test.js
Carrot7712 Jan 13, 2022
ad796af
feat:add R04.test.js
Carrot7712 Jan 13, 2022
7ece79e
add R01.test.js && unit-test-helper.js
AmberYen Mar 2, 2022
ef44c3e
add R03.test.js file
AmberYen Mar 2, 2022
4bce0c5
add R05.test.js
AmberYen Mar 2, 2022
79ff158
註解 typo 修正
zjzheng17 Jul 18, 2022
4fe60b0
correcting typo in comment
zjzheng17 Jul 18, 2022
03c14c0
correcting typo in comment
zjzheng17 Jul 18, 2022
7f6510a
fix R01.test.js comment typo
tuterwell Aug 10, 2023
a0468e4
feat: add index page
CYW-Allen Sep 7, 2023
a1ad60b
feat: add admin index page
CYW-Allen Sep 7, 2023
e1338df
feat: add user model
CYW-Allen Sep 7, 2023
8fe446a
feat: user signup
CYW-Allen Sep 7, 2023
5f7ca78
feat: flash msg & signup verification
CYW-Allen Sep 7, 2023
85c96f7
feat: passport init & signin
CYW-Allen Sep 7, 2023
895ffd7
feat: add header & footer
CYW-Allen Sep 7, 2023
c0ac872
feat: user model add is_admin
CYW-Allen Sep 7, 2023
b353e02
feat: add restaurant model
CYW-Allen Sep 7, 2023
9e60af8
feat: modify admin restaurants page
CYW-Allen Sep 7, 2023
72d8095
feat: create restaurant
CYW-Allen Sep 7, 2023
e6e852f
feat: admin restaurant page
CYW-Allen Sep 7, 2023
5517af4
feat: admin update restaurant
CYW-Allen Sep 7, 2023
4567f8a
feat: delete restaurant
CYW-Allen Sep 7, 2023
0959c4a
feat: add restaurant image
CYW-Allen Sep 7, 2023
117459e
feat: add seed data
CYW-Allen Sep 7, 2023
e07f4f5
rollback express-handlebars version
CYW-Allen Sep 8, 2023
2fcacb9
Merge remote-tracking branch 'origin/R01-test' into R01
CYW-Allen Sep 8, 2023
d20eea5
feat: adjust middleware/auth.js
CYW-Allen Sep 8, 2023
e1f393a
feat: admin user page and function (config isAdmin)
CYW-Allen Sep 9, 2023
b971ec7
feat: add category model
CYW-Allen Sep 10, 2023
4fc5245
feat: update seed files
CYW-Allen Sep 10, 2023
e3fa85d
feat: show category on admin restaurant pages
CYW-Allen Sep 10, 2023
dad8f52
feat: add categories selector on admin create and edit page
CYW-Allen Sep 10, 2023
9ef3435
feat: add ifConf hbs helper & update category selector
CYW-Allen Sep 10, 2023
c1908af
feat: add aadmin categories page & change view helper
CYW-Allen Sep 10, 2023
47edb2d
feat: category create
CYW-Allen Sep 10, 2023
220123d
feat: category update
CYW-Allen Sep 10, 2023
23d28dc
feat: category delete
CYW-Allen Sep 10, 2023
367e59a
feat: add restaurants index page
CYW-Allen Sep 11, 2023
6dc1796
feat: add restaurant page
CYW-Allen Sep 11, 2023
67ded44
feat: add dashboard page and display view counts
CYW-Allen Sep 11, 2023
990bd0a
feat: add categories navbar on restaurants index page
CYW-Allen Sep 11, 2023
1b4975a
feat: add pagination on restaurants index page, admin too
CYW-Allen Sep 12, 2023
485ca05
feat: add comment model
CYW-Allen Sep 12, 2023
e46ef81
feat: add post comment on restaurant page
CYW-Allen Sep 12, 2023
0814b62
feat: show comments on restaurant page
CYW-Allen Sep 13, 2023
2bc2057
feat: add delete comment for admin
CYW-Allen Sep 13, 2023
458b5aa
feat: add restaurant comments count in dashboard
CYW-Allen Sep 13, 2023
1b74cd8
Merge remote-tracking branch 'origin/R03-test' into R03
CYW-Allen Sep 14, 2023
c0c6579
fix for test requirements
CYW-Allen Sep 14, 2023
f7e3aa7
feat: add imgur image uploading handler and loading element for form …
CYW-Allen Sep 15, 2023
3774e9c
feat: add user profile
CYW-Allen Sep 16, 2023
cc500cc
feat: add commented restaurants information to user profile
CYW-Allen Sep 16, 2023
7888b0c
feat: add feeds page
CYW-Allen Sep 17, 2023
86f6c25
feat: create favorite model
CYW-Allen Sep 17, 2023
66833da
feat: add favorite/unfavorite button at index page
CYW-Allen Sep 17, 2023
dfd58e2
feat: get user's favorited restaurants & switch button
CYW-Allen Sep 17, 2023
c61a68d
feat: create followship model
CYW-Allen Sep 17, 2023
446c877
feat: add topUser page
CYW-Allen Sep 17, 2023
bee332d
feat: addFollowing & removeFollowning function
CYW-Allen Sep 18, 2023
8212473
feat: add like/unlike function
CYW-Allen Sep 18, 2023
87ca2ec
Merge remote-tracking branch 'origin/R05-test' into R05
CYW-Allen Sep 18, 2023
a7c68f5
fix: fulfill the test requirements
CYW-Allen Sep 18, 2023
0c85908
feat: add top 10 restaurants function
CYW-Allen Sep 22, 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
30 changes: 30 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,39 @@
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')
require('dotenv').config()

const passport = require('./config/passport')
const routes = require('./routes')
const handlebarsHelper = require('./helpers/handlerBars-helper')
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: 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
41 changes: 41 additions & 0 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const passport = require('passport')
const LocalStrategy = require('passport-local')
const bcrypt = require('bcryptjs')
const { User, Restaurant } = require('../models')

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, {
include: [
{ model: Restaurant, as: 'FavoritedRestaurants' },
{ model: User, as: 'Followers' },
{ model: User, as: 'Followings' },
{ model: Restaurant, as: 'LikedRestaurants' }
]
})
.then(user => cb(null, user.toJSON()))
.catch(err => cb(err))
})

module.exports = passport
131 changes: 131 additions & 0 deletions controllers/admin-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
const { Restaurant, User, Category } = require('../models')
const { imgurUploader } = require('../helpers/file-helper')
const { getOffset, getPagination } = require('../helpers/pagination-helper')

module.exports = {
async getRestaurants (req, res, next) {
try {
const DEFAULT_LIMIT = 7
const page = Number(req.query.page) || 1
const limit = Number(req.query.limit) || DEFAULT_LIMIT
const offset = getOffset(limit, page)

const restaurants = await Restaurant.findAndCountAll({
raw: true,
limit,
offset,
include: [Category],
nest: true
})

res.render('admin/restaurants', {
restaurants: restaurants.rows,
pagination: getPagination(limit, page, restaurants.count)
})
} catch (err) {
next(err)
}
},
async createRestaurant (_req, res) {
res.render('admin/create-restaurant', { categories: await Category.findAll({ raw: true }) })
},
async postRestaurant (req, res, next) {
try {
const { name, tel, address, openingHours, description, categoryId } = req.body

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

const filePath = await imgurUploader(req.file.buffer)
await Restaurant.create({ name, tel, address, openingHours, description, image: filePath || null, categoryId })
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,
include: [Category],
nest: 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, categories] = await Promise.all([
Restaurant.findByPk(req.params.id, { raw: true }),
Category.findAll({ raw: true })
])

if (!restaurant) throw new Error('The restaurant is not existed.')
res.render('admin/edit-restaurant', { restaurant, categories })
} catch (err) {
next(err)
}
},
async putRestaurant (req, res, next) {
try {
const { name, tel, address, openingHours, description, categoryId } = 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),
imgurUploader(req.file?.buffer)
])

if (!restaurant) throw new Error('The restaurant is not existed')
await restaurant.update({ name, tel, address, openingHours, description, image: filePath || restaurant.image, categoryId })
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 })
} 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)
}
}
}
53 changes: 53 additions & 0 deletions controllers/category-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const { Category } = require('../models')

module.exports = {
async getCategories (req, res, next) {
try {
const [categories, category] = await Promise.all([
Category.findAll({ raw: true }),
req.params.id ? Category.findByPk(req.params.id, { raw: true }) : null
])

res.render('admin/categories', { categories, category })
} catch (err) {
next(err)
}
},
async postCategory (req, res, next) {
try {
const { name } = req.body

if (!name || !name.replace(/\s/g, '').length) throw new Error('Category name is required')
await Category.create({ name })
req.flash('success_messages', 'A Category was successfully created')
res.redirect('/admin/categories')
} catch (err) {
next(err)
}
},
async putCategory (req, res, next) {
try {
const { name } = req.body
if (!name || !name.replace(/\s/g, '').length) throw new Error('Category name is required')

const category = await Category.findByPk(req.params.id)
if (!category) throw new Error('The category is not existed')
await category.update({ name })
req.flash('success_messages', 'The category was successfully to update')
res.redirect('/admin/categories')
} catch (err) {
next(err)
}
},
async deleteCategory (req, res, next) {
try {
const category = await Category.findByPk(req.params.id)

if (!category) throw new Error('The category is not existed')
await category.destroy()
res.redirect('/admin/categories')
} catch (err) {
next(err)
}
}
}
33 changes: 33 additions & 0 deletions controllers/comment-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { User, Restaurant, Comment } = require('../models')

module.exports = {
async postComment (req, res, next) {
try {
const userId = req.user.id
const { text, restaurantId } = req.body
if (!text) throw new Error('The comment is required')

const [user, restaurant] = await Promise.all([
User.findByPk(userId),
Restaurant.findByPk(restaurantId)
])
if (!user) throw new Error('The user does not exist')
if (!restaurant) throw new Error('The restaurant does not exist')
await Comment.create({ text, restaurantId, userId })
res.redirect(`/restaurants/${restaurantId}`)
} catch (err) {
next(err)
}
},
async deleteComment (req, res, next) {
try {
const comment = await Comment.findByPk(req.params.id)
if (!comment) throw new Error('The comment does not exist')
await comment.destroy()
req.flash('success_messages', 'Success to delete the comment')
res.redirect(`/restaurants/${comment.restaurantId}`)
} catch (err) {
next(err)
}
}
}
Loading