Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 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
6d3962d
Initialize project
dream184 Nov 22, 2021
7c9211d
feat: add admin, restaurant routes and controllers
dream184 Nov 22, 2021
a34c8cb
feat: create user model
dream184 Nov 22, 2021
e35ffd1
feat: add user signup function
dream184 Nov 22, 2021
39c8b71
feat: add user login function
dream184 Nov 22, 2021
98a9805
feat: add navbar layout
dream184 Nov 22, 2021
63ac2ea
feat: add isAdmin column to users, create admin routes and admin logi…
dream184 Nov 22, 2021
6778c7b
feat: create Restaurant model
dream184 Nov 22, 2021
4ad50c2
add admin user browses all restaurant and create new restaurant function
dream184 Nov 22, 2021
6ee8823
feat: add admin user can update and delete restaurant function
dream184 Nov 22, 2021
90220e1
feat: install multer and add upload picture function
dream184 Nov 23, 2021
978f196
feat: install faker and add user, restaurant seed data
dream184 Nov 23, 2021
c3419f6
heroku PG switch to ClearDB mysql
dream184 Nov 23, 2021
fbe785b
change port's env and add Procfile
dream184 Nov 23, 2021
edf035a
feat: install imgur-node-api and dotenv. Add image upload function
dream184 Nov 23, 2021
d9290db
fix confict
dream184 Nov 24, 2021
33ff482
A13: 使用者權限管理 (R01)
dream184 Nov 28, 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 .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IMGUR_CLIENT_ID=SKIP
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,
};
}
36 changes: 34 additions & 2 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,43 @@
const express = require('express')
const exphbs = require('express-handlebars')
const bodyParser = require('body-parser')
const flash = require('connect-flash')
const session = require('express-session')
const methodOverride = require('method-override')
const passport = require('./config/passport')
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.engine({ defaultLayout: 'main', extname: '.hbs' }))
app.set('view engine', 'hbs')

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

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

require('./routes')(app)
require('./routes')(app, passport)

module.exports = app
7 changes: 7 additions & 0 deletions config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
"dialect": "mysql"
},
"production": {
"use_env_variable": "CLEARDB_DATABASE_URL",
"protocol": "mysql",
"dialectOptions": {
"ssl": {
"rejectUnauthorized": false
}
},
"username": "root",
"password": null,
"database": "database_production",
Expand Down
35 changes: 35 additions & 0 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const passport = require('passport')
const LocalStrategy = require('passport-local')
const bcrypt = require('bcryptjs')
const db = require('../models')
const User = db.User

passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},

(req, username, password, done) => {
User.findOne({ where: { email: username } })
.then(user => {
if (!user) return done(null, false, req.flash('error_messages', '帳號輸入錯誤!'))
if (!bcrypt.compareSync(password, user.password)) return done(null, false, req.flash('error_messages', '密碼輸入錯誤!'))
return done(null, user)
})
}
))

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

passport.deserializeUser((id, done) => {
User.findByPk(id).then(user => {
user = user.toJSON()
return done(null, user)
})
})

module.exports = passport
144 changes: 144 additions & 0 deletions controllers/adminController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
const db = require('../models')
const Restaurant = db.Restaurant
const User = db.User
const fs = require('fs')
const imgur = require('imgur-node-api')
const user = require('../models/user')
const IMGUR_CLIENT_ID = process.env.IMGUR_CLIENT_ID

const adminController = {
getRestaurants: (req, res) => {
return Restaurant.findAll({ raw: true }).then(restaurants => {
return res.render('admin/restaurants', { restaurants: restaurants })
})
},
createRestaurant: (req, res) => {
return res.render('admin/create')
},
postRestaurant: (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.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', "restaurant was successfully created")
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,
imgae: null
})
.then((restaurant) => {
req.flash('success_messages', "restaurant was successfully created")
res.redirect('/admin/restaurants')
})
}
},
getRestaurant: (req, res) => {
return Restaurant.findByPk(req.params.id, { raw: true }).then(restaurant => {
return res.render('admin/restaurant', {
restaurant: restaurant
})
})
},
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', "restaurant was successfully update")
res.redirect('/admin/restaurants')
})
})
})
} 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: restaurant.image
})
.then((restaurant) => {
req.flash('success_messages', "restaurant was successfully update")
res.redirect('/admin/restaurants')
})
})
}
},
deleteRestaurant: (req, res) => {
return Restaurant.findByPk(req.params.id)
.then((restaurant) => {
restaurant.destroy()
.then((restaurant) => {
res.redirect('/admin/restaurants')
})
})
},
getUsers: (req, res) => {
return User.findAll({ raw: true }).then(users => {
return res.render('admin/users', { users: users })
})
},
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')
}
user.update({
isAdmin: user.isAdmin ? false : true
})
})
.then((user) => {
req.flash('success_messages', "使用者權限變更成功")
res.redirect('/admin/users')
})
}
}
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 = {
getRestaurant: (req, res) => {
return res.render('restaurants')
}
}

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

const userController = {
signUpPage: (req, res) => {
return res.render('signup')
},

signUp: (req, res) => {
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