Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 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
b54a2e9
add R02.test.js file
AmberYen Mar 2, 2022
ef44c3e
add R03.test.js file
AmberYen Mar 2, 2022
1ead393
add R04.test.js
AmberYen Mar 2, 2022
4bce0c5
add R05.test.js
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
326bed8
fix R01.test.js comment typo
tuterwell Aug 10, 2023
7f6510a
fix R01.test.js comment typo
tuterwell Aug 10, 2023
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
16bd368
chore: init github workflow
eugenechen0514 Sep 9, 2023
afe4d45
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
2942b99
Merge branch 'R03-test' into R04-test
eugenechen0514 Sep 17, 2023
8e195f8
Merge branch 'R04-test' into R05-test
eugenechen0514 Sep 17, 2023
1b8cd24
Delete .travis.yml
tuterwell Sep 18, 2023
f3b0c32
Delete .travis.yml
tuterwell Sep 18, 2023
a449027
Delete .travis.yml
tuterwell Sep 18, 2023
07201da
Delete .travis.yml
tuterwell Sep 18, 2023
4526b72
feat: add handlebars
syh053 Jul 24, 2024
193b26f
build front desk router
syh053 Jul 25, 2024
2aa403a
build backend router
syh053 Jul 25, 2024
b426619
build sequelize related folders & files
syh053 Jul 25, 2024
64c4bc8
add user model
syh053 Jul 26, 2024
95c6ab4
user signup
syh053 Jul 26, 2024
0a93540
flash message & signup verification
syh053 Jul 27, 2024
b9572ce
passport init & signin
syh053 Jul 28, 2024
9a0f185
add header & footer
syh053 Jul 28, 2024
22b5022
user model add isAdmin
syh053 Jul 29, 2024
8879e2f
build restaurants table
syh053 Jul 30, 2024
74947a5
modify admin restaurants page
syh053 Jul 31, 2024
00e3fc8
implement restaurant function
syh053 Aug 1, 2024
1526a24
implement admin restaurant router
syh053 Aug 3, 2024
77c1923
admin restaurant update function
syh053 Aug 4, 2024
88b04ed
admin delete restaurant function
syh053 Aug 4, 2024
24b4470
add restaurant image
syh053 Aug 6, 2024
301d7c9
build seed data
syh053 Aug 8, 2024
ecb8242
Merge remote-tracking branch 'upstream/R01-test' into R01
syh053 Aug 10, 2024
96455d5
adjust middleware/auth.js
syh053 Aug 10, 2024
7b41b47
build users page & change isAdmin function
syh053 Aug 10, 2024
16a1439
fix R01 related errors
syh053 Aug 11, 2024
95ff43f
add category model
syh053 Aug 13, 2024
11e8491
update seed files & fix some errors
syh053 Aug 14, 2024
8da1a2e
exhibit category on admin restaruant pages
syh053 Aug 16, 2024
34527a3
add categories selector & add ifCond hbs helper
syh053 Aug 17, 2024
27f7eff
add admin categories page
syh053 Aug 18, 2024
1316bc1
category create
syh053 Aug 18, 2024
2161cd8
update category function
syh053 Aug 18, 2024
d7de9c0
category delete
syh053 Aug 19, 2024
46b702b
build restaurants index page
syh053 Aug 23, 2024
20e7b5d
build restaurant page
syh053 Aug 24, 2024
7469152
Merge remote-tracking branch 'upstream/R02-test' into R02
syh053 Aug 24, 2024
669e9aa
build dashboard for view counter
syh053 Aug 25, 2024
71a46c7
add categories navbar on restaurants index page
syh053 Aug 26, 2024
e02f716
add pagination on restaurants index page
syh053 Aug 28, 2024
3f6c38b
add categories nabar on admin restaurants index page
syh053 Aug 30, 2024
12216b8
add admin pagination on restaurants index page
syh053 Aug 30, 2024
dbcb47e
add comment model
syh053 Aug 31, 2024
1b08da3
add post comment on restaurant page
syh053 Aug 31, 2024
da2d87e
show comments on restaurant page
syh053 Sep 1, 2024
b352b8d
add delete comment for admin
syh053 Sep 1, 2024
b3ff3a5
show number of comments & sort comments by time
syh053 Sep 1, 2024
afdade7
Merge remote-tracking branch 'upstream/R03-test' into R03
syh053 Sep 2, 2024
162d580
build profile page and be able to view and update
syh053 Sep 2, 2024
446fb4d
view restaurants comments by users on user page
syh053 Sep 5, 2024
8f28bd6
build feeds page
syh053 Sep 7, 2024
038afb4
create favorite model
syh053 Sep 7, 2024
fb68067
add favorite/unfavorite button at index page
syh053 Sep 8, 2024
b0fb753
get user's favorited restaurants & switch button
syh053 Sep 8, 2024
1727dff
add R04.test.js
AmberYen Mar 2, 2022
7e8d104
bulid Like model
syh053 Sep 9, 2024
bbe7c55
get user's liked restaurants & switch button
syh053 Sep 9, 2024
53e087e
create followship model
syh053 Sep 10, 2024
56228c0
add topUser page
syh053 Sep 14, 2024
22786c4
build addFollowing & removeFollowing function
syh053 Sep 15, 2024
a818831
Merge remote-tracking branch 'upstream/R05-test' into R05
syh053 Sep 15, 2024
8d3cf11
build top 10 restaurants page
syh053 Sep 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
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
8 changes: 8 additions & 0 deletions .sequelizerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const path = require('path')

module.exports = {
'config': path.resolve('config', 'database.js'),
'models-path': path.resolve('db/', 'models'),
'seeders-path': path.resolve('db/', 'seeders'),
'migrations-path': path.resolve('db/', 'migrations')
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"editor.tabSize": 2
}
50 changes: 50 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,59 @@
const express = require('express')
const routes = require('./routes')

const path = require('path')

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

const db = require('./db/models')

const handlebars = require('express-handlebars')

// 引入 helpers
const handlebarsHelpers = require('./helpers/handlebars-helpers')

// 註冊 Handlebars 樣板引擎,並指定副檔名為 .hbs
app.engine('.hbs', handlebars({ extname: '.hbs', helpers: handlebarsHelpers }))

// 設定使用 Handlebars 做為樣板引擎
app.set('view engine', '.hbs')

// 啟用 body-parser
app.use(express.urlencoded({ extended: true }))

// 載入 express-session 套件
const session = require('express-session')

// 載入 connect-flash 套件
const flash = require('connect-flash')

// 載入 message
const message = require('./middlewares/messages')

// 載入 passport
const passport = require('passport')

// 設定 SESSION_SECRET 還境變數
const SESSION_SECRET = 'secret'

// 設定 session
app.use(session({ secret: SESSION_SECRET, saveUninitialized: false, resave: false }))

app.use(flash())

app.use(passport.initialize())
app.use(passport.session())

// 載入 method-override
const methodOverride = require('method-override')

app.use(methodOverride('_method'))

app.use('/upload', express.static(path.join(__dirname, '/upload')))

app.use(message)

app.use(routes)

app.listen(port, () => {
Expand Down
32 changes: 32 additions & 0 deletions config/database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module.exports = {

development: {
username: 'root',
password: 'password',
database: 'forum',
host: '127.0.0.1',
dialect: 'mysql'
},
test: {
username: 'root',
password: null,
database: 'database_test',
host: '127.0.0.1',
dialect: 'mysql'
},
production: {
username: 'root',
password: null,
database: 'database_production',
host: '127.0.0.1',
dialect: 'mysql'
},
github: {
username: 'root',
password: 'password',
database: 'forum',
host: '127.0.0.1',
dialect: 'mysql',
logging: false
}
}
46 changes: 46 additions & 0 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const passport = require('passport')
const LocalStrategy = require('passport-local')

const { User, Restaurant } = require('../db/models')

const bcrypt = require('bcryptjs')

passport.use(new LocalStrategy({
usernameField: 'email',
passReqToCallback: true // 在下面的 callback function 中加入 req 屬性
},
(req, email, password, done) => {
User.findOne({
attributes: ['id', 'name', 'email', 'password'],
where: { email }
})
.then(user => {
if (!user) return done(null, false, req.flash('error_messages', 'email or passport input error!!'))

bcrypt.compare(password, user.password)
.then(ans => {
if (!ans) return done(null, false, req.flash('error_messages', 'email or passport input error!!'))
return done(null, user)
})
})
}))

passport.serializeUser((user, done) => {
return done(null, user.id)
})

passport.deserializeUser((id, done) => {
User.findByPk(id, {
// nest: true,
include: [
{ model: Restaurant, as: 'FavoritedRestaurants' },
{ model: Restaurant, as: 'LikeRestaurants' },
{ model: User, as: 'Followings' },
{ model: User, as: 'Followers' }
]
})
.then(user => done(null, user.toJSON()))
.catch(err => done(err))
})

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

const adminController = {
getRestaurant: (req, res, next) => {
return Restaurant.findByPk(req.params.id, {
raw: true,
nest: true,
include: Category
})
.then(restaurant => {
if (!restaurant) throw Error("Couldn't find any restaurant!!")
res.render('admin/restaurant', { restaurant })
})
.catch(err => {
err.name = '搜尋單筆餐廳'
err.message = '資料庫查找錯誤!!'
next(err)
})
},

editRestaurant: (req, res, next) => {
return Promise.all([
Restaurant.findByPk(req.params.id, {
raw: true,
nest: true,
include: Category
}),
Category.findAll({ raw: true })
])
.then(([restaurant, categories]) => {
if (!restaurant) throw Error("Couldn't find any restaurant!!")
res.render('admin/edit-restaurant', { restaurant, categories })
})
.catch(err => {
err.name = '編輯單筆餐廳'
err.message = '資料庫查找錯誤!!'
next(err)
})
},

putRestaurant: (req, res, next) => {
const { name, categoryId, tel, address, openingHours, description } = req.body
if (!name) throw new Error('Missing name!!')
if (!categoryId) throw new Error('Missing name!!')

const { file } = req // 把檔案取出來,也可以寫成 const file = req.file

return Promise.all([ // 非同步處理
Restaurant.findByPk(req.params.id), // 去資料庫查有沒有這間餐廳
localFileHandler(file)
])

.then(([restaurant, file]) => {
if (!restaurant) throw Error("Couldn't find any restaurant!!")

return restaurant.update({
name,
categoryId,
tel,
address,
openingHours,
description,
image: file || restaurant.image // 如果 filePath 是 Truthy (使用者有上傳新照片) 就用 filePath,是 Falsy (使用者沒有上傳新照片) 就沿用原本資料庫內的值
})
})
.then(() => {
req.flash('success_messages', 'restaurant edited successfully!!') // 在畫面顯示成功提示
res.redirect('/admin/restaurants')
})
.catch(err => {
err.name = 'editeError'
err.message = 'edited fail!!'
next(err)
})
},

getRestaurants: (req, res, next) => {
const categoryId = Number(req.query.categoryId) || ''
const page = Number(req.query.page) || 1
const DEFAULT_LIMIT = 10
const offset = getOffset(page, DEFAULT_LIMIT)
const limit = Number(req.query.limit) || DEFAULT_LIMIT

Promise.all([
Restaurant.findAndCountAll({
where: { ...categoryId ? { categoryId } : {} },
offset,
limit,
raw: true,
nest: true,
include: Category
}),
Category.findAll({ raw: true })
])
.then(([restaurants, categories]) => res.render('admin/restaurants', {
restaurants: restaurants.rows,
categories,
categoryId,
pagination: getPagination(limit, page, restaurants.count)
}))
.catch(err => {
err.name = '全部餐廳搜尋'
err.message = '資料庫錯誤!!'
next(err)
})
},

getUsers: (req, res, next) => {
return User.findAll({ raw: true })
.then(users => {
res.render('admin/users', { users })
})
.catch(err => {
err.name = '全部使用者搜尋'
err.message = '資料庫錯誤!!'
next(err)
})
},

createRestaurant: (req, res, next) => {
Category.findAll({ raw: true })
.then(categories => res.render('admin/create-restaurant', { categories }))
.catch(err => next(err))
},

postRestaurant: (req, res, next) => {
const { name, categoryId, tel, address, openingHours, description } = req.body

if (!name) throw new Error('Missing name!!')
if (!categoryId) throw new Error('Missing name!!')

const { file } = req // 把檔案取出來,也可以寫成 const file = req.file

return localFileHandler(file) // 把取出的檔案傳給 file-helper 處理後
.then(filePath => Restaurant.create({
name,
categoryId,
tel,
address,
openingHours,
description,
image: filePath
}))
.then(() => {
req.flash('success_messages', 'restaurant created successfully!!') // 在畫面顯示成功提示
res.redirect('/admin/restaurants') // 新增完成後導回後台首頁
})
.catch(err => {
err.name = 'createError'
err.message = 'created fail!!'
next(err)
})
},

patchUser: (req, res, next) => {
const { id } = req.params
return User.findByPk(id)
.then(user => {
if (!user) throw new Error("Couldn't find any user!!")
if (user.name === 'admin') {
req.flash('error_messages', '禁止變更 root 權限')
return res.redirect('back')
}
return user.update({
isAdmin: !user.isAdmin
})
})
.then(() => {
req.flash('success_messages', '使用者權限變更成功') // 在畫面顯示成功提示
return res.redirect('/admin/users')
})
.catch(err => next(err))
},

deleteRestaurant: (req, res, next) => {
return Restaurant.findByPk(req.params.id)
.then(restaurant => {
console.log(restaurant === null)
if (restaurant === null) throw new Error("Couldn't delete any restaurant!!")
return restaurant.destroy()
})
.then(() => {
req.flash('success_messages', 'Delete successfully!!')
res.redirect('/admin/restaurants')
})
.catch(err => next(err))
}
}

module.exports = adminController
Loading