Skip to content
Open

test #52

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
95105ff
add hbs
f88643 Dec 8, 2023
543a362
add index page
f88643 Dec 10, 2023
5277b84
add admin index page
f88643 Dec 10, 2023
c33a928
flash msg $signup verification
f88643 Dec 11, 2023
571faec
header & footer
f88643 Dec 11, 2023
1d39924
faker
f88643 Dec 12, 2023
3685bae
test
f88643 Dec 13, 2023
8da2b57
test update
f88643 Dec 13, 2023
d4b8742
database?
f88643 Dec 13, 2023
fd9f986
restaurant
f88643 Dec 13, 2023
b532543
restaurant
f88643 Dec 13, 2023
d7108fd
Restaurant
f88643 Dec 13, 2023
9b9737e
Restaurant
f88643 Dec 13, 2023
af8e818
Restaurant
f88643 Dec 14, 2023
d16b3c7
Restaurant
f88643 Dec 14, 2023
3b65df6
Restaurant
f88643 Dec 14, 2023
81e7bf4
seed update
f88643 Dec 14, 2023
3e20df2
render category
f88643 Dec 14, 2023
d380d79
test update
f88643 Dec 15, 2023
1c5478b
test update
f88643 Dec 15, 2023
3d14ec2
test update
f88643 Dec 15, 2023
d77deb8
test update
f88643 Dec 15, 2023
f78c470
test update
f88643 Dec 15, 2023
0e11625
test update
f88643 Dec 15, 2023
aa3ab72
test update
f88643 Dec 15, 2023
b0fdd1b
test update
f88643 Dec 15, 2023
f78b619
add categories
f88643 Dec 15, 2023
873f9bb
test
f88643 Dec 16, 2023
7301de8
test
f88643 Dec 16, 2023
e96aae6
test
f88643 Dec 18, 2023
14e22de
Merge branch 'R01' of https://github.com/f88643/forum-express-grading…
f88643 Dec 18, 2023
4d71835
test
f88643 Dec 18, 2023
573562d
test
f88643 Dec 18, 2023
5e8b4bc
test
f88643 Dec 18, 2023
aa0e9d1
test
f88643 Dec 18, 2023
20e6c3e
test
f88643 Dec 18, 2023
d59dc3d
test
f88643 Dec 18, 2023
248091f
add dasgboard
f88643 Dec 18, 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
46 changes: 46 additions & 0 deletions .github/workflows/ci.yml.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Node.js test

on:
push:
branches:
# - main
- "*-test"
pull_request:
branches:
# - main

env:
NODE_ENV: github

jobs:
test:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [14.x] # ex: [16.x, 18.x], See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: mirromutth/mysql-action@v1.1
with:
host port: 3306 # Optional, default value is 3306. The port of host
container port: 3307 # Optional, default value is 3306. The port of container
character set server: "utf8mb4" # Optional, default value is 'utf8mb4'. The '--character-set-server' option for mysqld
collation server: "utf8mb4_unicode_ci" # Optional, default value is 'utf8mb4_general_ci'. The '--collation-server' option for mysqld
mysql version: "8.0" # Optional, default value is "latest". The version of the MySQL
mysql database: "forum" # Optional, default value is "test". The specified database which will be create
mysql root password: "password" # Required if "mysql user" is empty, default is empty. The root superuser password
# 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
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- run: npm install
- run: npm install sequelize-cli -g
- run: sequelize db:migrate
- run: sequelize db:seed:all
- run: npm test
30 changes: 29 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
const path = require('path')
const express = require('express')
const handlebars = require('express-handlebars')
const flash = require('connect-flash')
const methodOverride = require('method-override')
const session = require('express-session')
const passport = require('./config/passport')
const handlebarsHelpers = require('./helpers/handlebars-helpers')
const { getUser } = require('./helpers/auth-helpers')
const routes = require('./routes')


const app = express()
const port = process.env.PORT || 3000
const SESSION_SECRET = 'secret'
// 註冊 Handlebars 樣板引擎,並指定副檔名為 .hbs
app.engine('hbs', handlebars({ extname: '.hbs', helpers: handlebarsHelpers }))
// 設定使用 Handlebars 做為樣板引擎
app.set('view engine', 'hbs')
app.use(methodOverride('_method'))
app.use(express.urlencoded({ extended: true }))

app.use(session({ secret: SESSION_SECRET, resave: false, saveUninitialized: false }))
app.use(passport.initialize()) // 初始化 Passport
app.use(passport.session()) // 啟動 session 功能
app.use(flash())
app.use('/upload', express.static(path.join(__dirname, 'upload')))
app.use((req, res, next) => {
res.locals.success_messages = req.flash('success_messages') // 設定 success_msg 訊息
res.locals.error_messages = req.flash('error_messages') // 設定 warning_msg 訊息
res.locals.user = getUser(req)
next()
})

app.use(routes)

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

module.exports = app
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

passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
(req, username, password, cb) => {
User.findOne({ where: { email: username } })
.then(user => {
if (!user) return cb(null, false, req.flash('error_messages', '帳號或密碼輸入錯誤!'))
bcrypt.compare(password, user.password)
.then(res => {
if (!res) 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)
.then(user => {
user = user.toJSON() // findByPk會回傳sequelize model,而不是單純的user object。toJSON()可以移除sequelize的特性。
return cb(null, user)
})
})

module.exports = passport
38 changes: 38 additions & 0 deletions config/passport.js.bak
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

passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
(req, username, password, cb) => {
User.findOne({ where: { email: username } })
.then(user => {
if (!user) return cb(null, false, req.flash('error_messages', '帳號或密碼輸入錯誤!'))
bcrypt.compare(password, user.password)
.then(res => {
if (!res) 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)
.then(user => {
user = user.toJSON() // findByPk會回傳sequelize model,而不是單純的user object。toJSON()可以移除sequelize的特性。
return cb(null, user)
})
})

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

const adminController = {
getRestaurants: (req, res, next) => {
Restaurant.findAll({
raw: true,
nest: true, // restaurant['category.name'] => restaurant.category.name
include: [Category]

})
.then(restaurants => res.render('admin/restaurants', { restaurants }))
.catch(err => next(err))
},
createRestaurant: (req, res, next) => {
return Category.findAll({
raw: true
})
.then(categories => res.render('admin/create-restaurant', { categories }))
.catch(err => next(err))
},
postRestaurant: (req, res, next) => {
const { name, tel, address, openingHours, description, categoryId } = req.body // 從 req.body 拿出表單裡的資料
if (!name) throw new Error('Restaurant name is required!') // name 是必填,若發先是空值就會終止程式碼,並在畫面顯示錯誤提示

const { file } = req

localFileHandler(file)
.then(filePath => Restaurant.create({ // 產生一個新的 Restaurant 物件實例,並存入資料庫
name,
tel,
address,
openingHours,
description,
image: filePath || null,
categoryId
})
)

.then(() => {
req.flash('success_messages', 'restaurant was successfully created') // 在畫面顯示成功提示
res.redirect('/admin/restaurants') // 新增完成後導回後台首頁
})
.catch(err => next(err))
},
getRestaurant: (req, res, next) => {
Restaurant.findByPk(req.params.id, { // 去資料庫用 id 找一筆資料
raw: true, // 找到以後整理格式再回傳
nest: true,
include: [Category]

})
.then(restaurant => {
if (!restaurant) throw new Error("Restaurant didn't exist!") // 如果找不到,回傳錯誤訊息,後面不執行
res.render('admin/restaurant', { restaurant })
})
.catch(err => next(err))
},
editRestaurant: (req, res, next) => {
Promise.all([
Restaurant.findByPk(req.params.id, { raw: true }),
Category.findAll({ raw: true })
])
.then(([restaurant, categories]) => {
if (!restaurant) throw new Error("Restaurant didn't exist!")
res.render('admin/edit-restaurant', { restaurant, categories })
})

.catch(err => next(err))
},
putRestaurant: (req, res, next) => {
const { name, tel, address, openingHours, description, categoryId } = req.body
if (!name) throw new Error('Restaurant name is required!')
const { file } = req // 把檔案取出來
Promise.all([ // 非同步處理
Restaurant.findByPk(req.params.id), // 去資料庫查有沒有這間餐廳
localFileHandler(file) // 把檔案傳到 file-helper 處理
])
.then(([restaurant, filePath]) => { // 以上兩樣事都做完以後
if (!restaurant) throw new Error("Restaurant didn't exist!")
return restaurant.update({ // 修改這筆資料
name,
tel,
address,
openingHours,
description,
image: filePath || restaurant.image, // 如果 filePath 是 Truthy (使用者有上傳新照片) 就用 filePath,是 Falsy (使用者沒有上傳新照片) 就沿用原本資料庫內的值
categoryId
})
})
.then(() => {
req.flash('success_messages', 'restaurant was successfully updated')
res.redirect('/admin/restaurants')
})
},
deleteRestaurant: (req, res, next) => {
return Restaurant.findByPk(req.params.id)
.then(restaurant => {
if (!restaurant) throw new Error("Restaurant didn't exist!")
return restaurant.destroy()
})
.then(() => {
req.flash('success_messages', 'restaurant was successfully deleted')
res.redirect('/admin/restaurants')
})
.catch(err => next(err))
},
getUsers: (req, res, next) => {
return User.findAll({
raw: true
})
.then(users => {
res.render('admin/users', { users }
)
})
.catch(err => next(err))
},

patchUser: (req, res, next) => {
return User.findByPk(req.params.id) // 去資料庫查有沒有這間user
.then(user => {
if (!user) throw new Error("User didn't exist!")
if (user.email === 'root@example.com') {
req.flash('error_messages', '禁止變更 root 權限')
return res.redirect('back')
}
return user.update({ isAdmin: !user.isAdmin })
})
.then(() => {
req.flash('success_messages', '使用者權限變更成功')
res.redirect('/admin/users')
})
.catch(err => next(err))
}
}
module.exports = adminController
48 changes: 48 additions & 0 deletions controllers/category-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const { Category } = require('../models')

const categoryController = {
getCategories: (req, res, next) => {
return Promise.all([
Category.findAll({ raw: true }),
req.params.id ? Category.findByPk(req.params.id, { raw: true }) : null
])
.then(([categories, category]) => {
res.render('admin/categories', {
categories,
category
})
})
.catch(err => next(err))
},
postCategory: (req, res, next) => {
const { name } = req.body // 從 req.body 拿出表單裡的資料
if (!name) throw new Error('Category name is required!') // name 是必填,若發先是空值就會終止程式碼,並在畫面顯示錯誤提示
return Category.create({ name })
.then(() => {
req.flash('success_messages', 'Category was successfully created') // 在畫面顯示成功提示
res.redirect('/admin/categories') // 新增完成後導回後台首頁
})
.catch(err => next(err))
},
putCategory: (req, res, next) => {
const { name } = req.body
if (!name) throw new Error('Category name is required!')
return Category.findByPk(req.params.id)
.then(category => {
if (!category) throw new Error("Category doesn't exist!")
return category.update({ name })
})
.then(() => res.redirect('/admin/categories'))
.catch(err => next(err))
},
deleteCategory: (req, res, next) => {
return Category.findByPk(req.params.id)
.then(category => {
if (!category) throw new Error("Category didn't exist!") // 反查,確認要刪除的類別存在,再進行下面刪除動作
return category.destroy()
})
.then(() => res.redirect('/admin/categories'))
.catch(err => next(err))
}
}
module.exports = categoryController
Loading