Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
4111b32
add proxyquire & sequelize-mock npm lib
AmberYen Oct 12, 2021
161a837
add new A17 test
AmberYen Oct 12, 2021
bb8ce89
tests/A17.test.js added comments
AmberYen Oct 18, 2021
c15fe79
add A19 test
AmberYen Oct 18, 2021
da713e0
add A20 test
AmberYen Oct 18, 2021
88812da
add A21 test
AmberYen Oct 18, 2021
a7d56e1
add A22 test
AmberYen Oct 18, 2021
d4a7391
add commnets for A17
AmberYen Oct 19, 2021
e981eb0
add comments for A19
AmberYen Oct 19, 2021
5396cf6
add comments for A22
AmberYen Oct 19, 2021
dab9f1d
add comments for A21
AmberYen Oct 19, 2021
c7a336d
add comments for A22
AmberYen Oct 19, 2021
a175811
adjust coding style
Carrot7712 Oct 27, 2021
39950b9
修改變數名與註解
Carrot7712 Oct 27, 2021
82de6c8
refactor mock db in unit test / integrated into helpers/unitTestHelpers
AmberYen Oct 28, 2021
c9b201e
remove n_comments check due to follow model answer
AmberYen Oct 28, 2021
50476ab
revise comments
Carrot7712 Oct 28, 2021
b3ddfcd
add A21 test userId and restaurantId with addLike function
AmberYen Oct 29, 2021
6c5f774
add viewCounts check in A20
AmberYen Oct 29, 2021
2ad4b59
add new test comment
AmberYen Oct 29, 2021
a2df6a4
remove other test and rename A17 -> R01
AmberYen Oct 31, 2021
9148715
fixed R01 ../../app issue & not found sequelize lib
AmberYen Nov 1, 2021
ba68733
fixed controller path wrong issue / check data exist
AmberYen Nov 1, 2021
e45367f
add mockRequest / mockResponse
AmberYen Nov 1, 2021
d487f4a
use new mockRequest / mockResponse
AmberYen Nov 1, 2021
73fc123
require sinon
AmberYen Nov 1, 2021
5224b9a
remove sinon
AmberYen Nov 1, 2021
9bf30ca
return [defaulvalue] when findall data empty
AmberYen Nov 1, 2021
05b87a2
remove done param
AmberYen Nov 2, 2021
a1cf9c3
update readme config.json package.json
Carrot7712 Nov 3, 2021
5e1573a
Update .travis.yml
Carrot7712 Nov 5, 2021
c71da44
Update .travis.yml
Carrot7712 Nov 5, 2021
29dd825
Update config.json
Carrot7712 Nov 8, 2021
9c63138
proj init
mingmoth Dec 13, 2021
eb7508f
add: views:hbs/ controllers:restControllers & import into router
mingmoth Dec 13, 2021
210ca17
add: adminRestaurants.hbs contorllers & router
mingmoth Dec 14, 2021
7a840a3
add: import db into app.js & notes in models/index.js
mingmoth Dec 14, 2021
88db314
create: user model in MySQL & migrate
mingmoth Dec 14, 2021
6444706
add: signup.hbs and userControllers for signup function
mingmoth Dec 14, 2021
cc83e5b
add: install express-session/flash & add signup certification
mingmoth Dec 14, 2021
eb066be
add: passport/-local & set userController signin function & router
mingmoth Dec 16, 2021
4627627
add: Navbar & import user in app.js
mingmoth Dec 16, 2021
321f535
add: isAdmin to users table & authentication before rendering pages
mingmoth Dec 16, 2021
23e1b3a
create: Reataurant model
mingmoth Dec 17, 2021
84def98
add: AdminRestaurants.hbs
mingmoth Dec 17, 2021
d1ce749
add: adminCreate.hbs & create restaurant function
mingmoth Dec 17, 2021
3cdd28b
update: adminCreate.hbs for editing & install method-override & add p…
mingmoth Dec 17, 2021
04b47eb
add adminRestaurant delete function
mingmoth Dec 17, 2021
17be220
add: image into restaurants model & install multer & upload image fea…
mingmoth Dec 21, 2021
d8f4ce1
add 3 users seed data
mingmoth Dec 21, 2021
76e09b6
add: Install faker and add restaurant seed data
mingmoth Dec 21, 2021
8d403d5
add npm run rebuild
mingmoth Dec 22, 2021
fe4b767
Install pg and update production config
mingmoth Dec 22, 2021
bcb7bd8
create heroku app & set process.env Procfile
mingmoth Dec 22, 2021
1203203
install imgur-node-api & set imgur upload feature in adminControllers
mingmoth Dec 22, 2021
2e00a57
install dotenv & set node_env in app.js
mingmoth Dec 22, 2021
93ae6de
heroku pg switch to cleardb mysql
mingmoth Dec 22, 2021
bdc1012
merge R01 with main
mingmoth Dec 22, 2021
b5933f7
add admin users.hbs & related controller & router
mingmoth Dec 22, 2021
ad83f8e
add user toggleAdmin feature
mingmoth Dec 22, 2021
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
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: NODE_ENV=production node app.js
6 changes: 3 additions & 3 deletions _helpers.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
function ensureAuthenticated(req) {
return req.isAuthenticated();
return req.isAuthenticated()
}

function getUser(req) {
return req.user;
return req.user
}

module.exports = {
ensureAuthenticated,
getUser,
};
}
41 changes: 38 additions & 3 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,46 @@
const express = require('express')
const exphbs = require('express-handlebars')
const db = require('./models')
const flash = require('connect-flash')
const session = require('express-session')
const passport = require('./config/passport')
const methodOverride = require('method-override')
const app = express()
const port = 3000
const port = process.env.PORT || 3000

if (process.env.NODE_ENV !== 'production') {
require('dotenv').config()
}

app.engine('hbs', exphbs({extname: '.hbs'}))
app.set('view engine', 'hbs')

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

// 我們需要用到「快閃訊息 (flash message)」,這種 message 存在 session 裡面
// set session and flash
app.use(session({ secret: 'secret', resave: false, saveUninitialized: false }))
app.use(flash())
// set passport
app.use(passport.initialize())
app.use(passport.session())
// set method-override
app.use(methodOverride('_method'))
// 把 req.flash 放到 res.locals 裡面
app.use((req, res, next) => {
res.locals.success_messages = req.flash('success_messages')
res.locals.error_messages = req.flash('error_messages')
res.locals.user = req.user
next()
})
// /upload 這組路由,這裡因為是靜態檔案,所以不需要像其他的路由一樣寫 controller 邏輯,直接用 express.static 來指定路徑就可以了
app.use('/upload', express.static(__dirname + '/upload'))

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

require('./routes')(app)
// 把 passport 傳入 routes
require('./routes')(app, passport)

module.exports = app
17 changes: 11 additions & 6 deletions config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
"password": "password",
"database": "forum",
"host": "127.0.0.1",
"dialect": "mysql"
"dialect": "mysql",
"logging": true,
"logQueryParameter": false
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
"use_env_variable": "CLEARDB_DATABASE_URL",
"dialect": "postgres",
"protocol": "postgres",
"dialectOptions": {
"ssl": {
"rejectUnauthorized": false
}
}
},
"travis": {
"username": "travis",
Expand Down
38 changes: 38 additions & 0 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const passport = require('passport')
const LocalStrategy = require('passport-local')
const bcrypt = require('bcryptjs')

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

// setup passport strategy
// passport.use(new LocalStrategy(option: 設定客製化選項, function 登入認證程序))
passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
// 設置了 flash message,現在也想沿用,必須在 passport 的驗證邏輯裡,拿到 req 這個參數
// 設定了 passReqToCallback: true,就可以 callback 的第一個參數裡拿到 req,這麼一來我們就可以呼叫 req.flash() 把想要客製化的訊息放進去
passReqToCallback: true
},
(req, username, password, cb) => {
User.findOne({where: { email: username }}).then(user => {
if (!user) return cb(null, false, req.flash('error_messages', '帳號或密碼輸入錯誤'))
if (!bcrypt.compareSync(password, user.password)) 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
142 changes: 142 additions & 0 deletions controllers/adminController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
const db = require('../models')
const fs = require('fs')
const imgur = require('imgur-node-api')
const IMGUR_CLIENT_ID = process.env.IMGUR_CLIENT_ID
const Restaurant = db.Restaurant
const User = db.User

const adminController = {
// get all Users
getUsers: (req, res) => {
return User.findAll({raw: true}).then((users) => {
return res.render('admin/users', { users: users })
})
},
// toggle user isAdmin
toggleAdmin: (req, res) => {
return User.findByPk(req.params.id).then((user) => {
if (user.email === 'root@example.com') {
req.flash('error_messages', "禁止變更管理者權限")
return res.redirect('back')
} else {
user.update({
isAdmin: !user.isAdmin
}).then((user) => {
req.flash('success_messages', '使用者權限變更成功')
res.redirect('/admin/users')
})
}
})
},
// get all restaurants
getRestaurants: (req, res) => {
return Restaurant.findAll({ raw: true }).then(restaurants => {
return res.render('admin/restaurants', { restaurants: restaurants })
})
},
// get one restaurant
getRestaurant: (req, res) => {
return Restaurant.findByPk(req.params.id, { raw: true }).then(restaurant => {
return res.render('admin/restaurant', { restaurant: restaurant })
})
},
// create page
createRestaurant: (req, res) => {
return res.render('admin/create')
},
// create new restaurant
postRestaurant: (req, res) => {
if (!req.body.name) {
req.flash('error_messages', "請填寫餐廳名稱")
return res.redirect('back')
}
const { file } = req
if (file) {
imgur.setClientID(IMGUR_CLIENT_ID)
imgur.upload(file.path, (err, img) => {
return Restaurant.create({
name: req.body.name,
tel: req.body.tel,
address: req.body.address,
opening_hours: req.body.opening_hours,
description: req.body.description,
image: file ? img.data.link : null
}).then((restaurant) => {
req.flash('success_messages', '成功新增餐廳與圖片')
return res.redirect('/admin/restaurants')
})
})
} else {
return Restaurant.create({
name: req.body.name,
tel: req.body.tel,
address: req.body.address,
opening_hours: req.body.opening_hours,
description: req.body.description,
image: null
})
.then((restaurant) => {
req.flash('success_messages', '成功新增餐廳')
res.redirect('/admin/restaurants')
})
}
},
editRestaurant: (req, res) => {
return Restaurant.findByPk(req.params.id, { raw: true }).then((restaurant) => {
return res.render('admin/create', { restaurant: restaurant })
})
},
putRestaurant: (req, res) => {
if (!req.body.name) {
req.flash('error_messages', "name didn't exist")
return res.redirect('back')
}
const { file } = req
if (file) {
imgur.setClientID(IMGUR_CLIENT_ID);
imgur.upload(file.path, (err, img) => {
return Restaurant.findByPk(req.params.id).then((restaurant) => {
restaurant.update({
name: req.body.name,
tel: req.body.tel,
address: req.body.address,
opening_hours: req.body.opening_hours,
description: req.body.description,
image: file ? img.data.link : restaurant.image
}).then((restaurant) => {
req.flash('success_messages', '已成功更新餐廳資訊與圖片')
res.redirect(`/admin/restaurants/${req.params.id}`)
})
})
})
} else {
return Restaurant.findByPk(req.params.id)
.then((restaurant) => {
restaurant.update({
name: req.body.name,
tel: req.body.tel,
address: req.body.address,
opening_hours: req.body.opening_hours,
description: req.body.description,
image: null
})
.then((restaurant) => {
req.flash('success_messages', '已成功更新餐廳資訊')
res.redirect(`/admin/restaurants/${req.params.id}`)
})
})
}
},
deleteRestaurant: (req, res) => {
return Restaurant.findByPk(req.params.id)
.then((restaurant) => {
restaurant.destroy()
.then((restaurant) => {
req.flash('success_messages', '成功刪除餐廳')
res.redirect('/admin/restaurants')
})
})
}
}

module.exports = adminController
7 changes: 7 additions & 0 deletions controllers/restController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const restController = {
getRestaurants: (req, res) => {
return res.render('restaurants')
}
}

module.exports = restController
52 changes: 52 additions & 0 deletions controllers/userController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const bcrypt = require('bcryptjs')
const req = require('express/lib/request')
const { resetWatchers } = require('nodemon/lib/monitor/watch')
const db = require('../models')
const User = db.User

const userController = {
SignUpPage: (req, res) => {
return res.render('signup')
},
SignUp: (req, res) => {
// confirm
if (req.body.passwordCheck !== req.body.password) {
req.flash('error_messages', '兩次密碼輸入不同')
return res.redirect('/signup')
} else {
User.findOne({ where: { email: req.body.email } })
.then(user => {
if (user) {
req.flash('error_messages', '信箱重複!')
return res.redirect('/signup')
} else {
User.create({
name: req.body.name,
email: req.body.email,
password: bcrypt.hashSync(req.body.password, bcrypt.genSaltSync(10), null)
}).then(user => {
req.flash('success_messages', '成功註冊帳號!')
return res.redirect('/signin')
})
}
})
}
},
signInPage: (req, res) => {
return res.render('signin')
},

signIn: (req, res) => {
req.flash('success_messages', '成功登入!')
res.redirect('/restaurants')
},

logout: (req, res) => {
req.flash('success_messages', '登出成功!')
req.logout()
res.redirect('/signin')
}

}

module.exports = userController
Loading