Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
093c203
project init
mmm999xp Aug 21, 2023
910e9b6
add process.env.PORT
mmm999xp Aug 21, 2023
c3b03ba
feat: add error-handler
keitakeiko Aug 22, 2023
6067532
feat: add multer function
keitakeiko Aug 22, 2023
3f70eb4
feat: modify model
mmm999xp Aug 22, 2023
2b71773
Merge pull request #1 from mmm999xp/R01
mmm999xp Aug 22, 2023
f7365d1
Merge pull request #2 from mmm999xp/R02
mmm999xp Aug 22, 2023
a78f14f
feat: add-banner-to-users
mmm999xp Aug 22, 2023
93d906f
Merge pull request #4 from mmm999xp/R04
keitakeiko Aug 22, 2023
9f2510d
feat: passport Strategy
mmm999xp Aug 22, 2023
8b1048f
Merge pull request #5 from mmm999xp/R05
keitakeiko Aug 22, 2023
451a1d1
feat: add user signup
mmm999xp Aug 22, 2023
b2b3b49
Merge pull request #6 from mmm999xp/R06
keitakeiko Aug 23, 2023
6da7fd6
feat: add signin & api-error-handler (#7)
mmm999xp Aug 23, 2023
43a276b
feat: add getTweets function
keitakeiko Aug 25, 2023
125773f
Merge pull request #10 from mmm999xp/R08
mmm999xp Aug 25, 2023
565ed0e
R10 (#12)
mmm999xp Aug 25, 2023
18fd743
fix: amend getTweets function
keitakeiko Aug 25, 2023
1f9b723
Merge pull request #13 from mmm999xp/R08
mmm999xp Aug 25, 2023
eb12369
feat: add postTweet
keitakeiko Aug 25, 2023
1c00107
Merge branch 'main' into R11-1
mmm999xp Aug 26, 2023
8c85bc6
Merge pull request #14 from mmm999xp/R11-1
mmm999xp Aug 26, 2023
a303cfa
feat: add getTweet
keitakeiko Aug 26, 2023
0b05206
fix: amend router
keitakeiko Aug 26, 2023
084ebba
Merge branch 'main' into R12
mmm999xp Aug 26, 2023
74f7aca
Merge pull request #16 from mmm999xp/R12
mmm999xp Aug 26, 2023
159fa58
fix: amend function in getTweets & postTweet
keitakeiko Aug 26, 2023
80c2014
fix: amend function in getTweets & postTweet
keitakeiko Aug 26, 2023
93e2448
Merge pull request #17 from mmm999xp/R12
mmm999xp Aug 26, 2023
367ecff
feat: add likeTweet function and fix datetime-helper
keitakeiko Aug 26, 2023
272de57
Merge branch 'main' into R13
mmm999xp Aug 26, 2023
188e363
Merge pull request #18 from mmm999xp/R13
mmm999xp Aug 26, 2023
c82d388
feat: modify config for heroku (#19)
mmm999xp Aug 26, 2023
0a7c8a6
fix: seed bug fixed (#20)
mmm999xp Aug 26, 2023
6bdfaa6
feat: add unlikeTweet function
keitakeiko Aug 26, 2023
80d6289
feat: add getReplies
keitakeiko Aug 26, 2023
1e27afb
feat: add test function
keitakeiko Aug 27, 2023
b9e2aec
feat: add postReply
keitakeiko Aug 27, 2023
291ff4b
Merge branch 'main' into R14
mmm999xp Aug 27, 2023
c04e629
Merge pull request #21 from mmm999xp/R14
mmm999xp Aug 27, 2023
b9ce168
Merge pull request #22 from mmm999xp/R15
mmm999xp Aug 27, 2023
20655d5
Merge branch 'main' into R25
mmm999xp Aug 27, 2023
a6d2863
Merge pull request #23 from mmm999xp/R25
mmm999xp Aug 27, 2023
0baf00a
fix: adapt unlike function method to fit the test file
keitakeiko Aug 27, 2023
7439644
Merge pull request #24 from mmm999xp/R26
mmm999xp Aug 27, 2023
bd952e3
R16 (#25)
mmm999xp Aug 27, 2023
33e5f4d
R17 (#26)
mmm999xp Aug 27, 2023
efcd407
fix: adapt Reply, Like, Tweet function to fit the test file
keitakeiko Aug 27, 2023
3053079
R29 modify: passport config and helper (#28)
mmm999xp Aug 27, 2023
738d6c2
Merge branch 'main' into R27
keitakeiko Aug 27, 2023
78765fe
Merge pull request #27 from mmm999xp/R27
mmm999xp Aug 27, 2023
f7852a6
fix: amend function for signin & pass the getTweets
keitakeiko Aug 27, 2023
4963ba7
Merge pull request #29 from mmm999xp/R30
mmm999xp Aug 27, 2023
5ba7336
R21 (#30)
mmm999xp Aug 28, 2023
5c4e55e
feat: addFollowing function & CORS
keitakeiko Aug 28, 2023
2bed8ba
feat: add removeFollowing
keitakeiko Aug 28, 2023
f5771a4
Merge pull request #31 from mmm999xp/R31
mmm999xp Aug 28, 2023
9387b76
Merge pull request #32 from mmm999xp/R32
mmm999xp Aug 28, 2023
42b8cbc
feat: install cors
mmm999xp Aug 28, 2023
b9c0002
fix: error-msg in tweet-controller & followship-controller & add note
keitakeiko Aug 28, 2023
c0a6279
Merge pull request #33 from mmm999xp/R33
mmm999xp Aug 28, 2023
75eb517
Merge branch 'main' of https://github.com/mmm999xp/twitter-api-2023
mmm999xp Aug 28, 2023
ad57514
feat: add imgur & multer function
keitakeiko Aug 28, 2023
6f4a7e8
R36 (#35)
mmm999xp Aug 29, 2023
cbd2d38
Merge branch 'main' into R34
keitakeiko Aug 29, 2023
29f09b6
feat: add getTopFollow
keitakeiko Aug 29, 2023
a61bb47
Merge branch 'main' into R37
mmm999xp Aug 29, 2023
0ff0d9c
Merge pull request #37 from mmm999xp/R37
mmm999xp Aug 29, 2023
7d7de77
Merge branch 'main' into R34
mmm999xp Aug 29, 2023
699fadd
Merge pull request #36 from mmm999xp/R34
mmm999xp Aug 29, 2023
253e365
Merge branch 'main' of https://github.com/mmm999xp/twitter-api-2023
mmm999xp Aug 29, 2023
da88dc4
fix: npm install imgur
mmm999xp Aug 29, 2023
efb29f5
R38 fix: npm i imgur (#38)
mmm999xp Aug 29, 2023
7402934
fix: add front-end data
keitakeiko Aug 29, 2023
1133bf1
Merge pull request #39 from mmm999xp/R39
mmm999xp Aug 29, 2023
f9f78a5
Merge branch 'main' of https://github.com/mmm999xp/twitter-api-2023
mmm999xp Aug 29, 2023
3fa9d14
R40 (#40)
mmm999xp Aug 29, 2023
b20e989
Merge branch 'main' of https://github.com/mmm999xp/twitter-api-2023
mmm999xp Aug 29, 2023
6702894
R40 -fix: cors web address (#41)
mmm999xp Aug 29, 2023
7ab5132
Merge branch 'main' of https://github.com/mmm999xp/twitter-api-2023
mmm999xp Aug 29, 2023
6790224
R42 (#43)
mmm999xp Aug 30, 2023
a7766fe
fix req.params.id data type
keitakeiko Aug 31, 2023
1eec8bb
Merge branch 'main' into R43
mmm999xp Aug 31, 2023
9b77178
Merge pull request #44 from mmm999xp/R43
mmm999xp Aug 31, 2023
cec0505
modify: getUserReplies 、getUserLikes
mmm999xp Sep 1, 2023
7824da7
Merge pull request #45 from mmm999xp/R44
mmm999xp Sep 1, 2023
0ee4d61
fix: user -> User
mmm999xp Sep 1, 2023
e88f16c
Merge pull request #46 from mmm999xp/R44
mmm999xp Sep 1, 2023
c34e3a5
modify: getUserLikes add tweet description
mmm999xp Sep 2, 2023
b0532a9
Merge pull request #47 from mmm999xp/R44
mmm999xp Sep 2, 2023
27c6119
modify: res User & Tweet
mmm999xp Sep 2, 2023
3c80693
Merge pull request #48 from mmm999xp/R44
mmm999xp Sep 2, 2023
edb83cc
modify: add order DESC
mmm999xp Sep 2, 2023
2259e4b
fix: amend params code (#49)
keitakeiko Sep 2, 2023
04cd509
modify: add admin tweet
mmm999xp Sep 2, 2023
e0953da
fix: debug Number()
keitakeiko Sep 2, 2023
cc00d1b
Merge branch 'main' of https://github.com/mmm999xp/twitter-api-2023
keitakeiko Sep 2, 2023
498ce48
modify admin getTweets
mmm999xp Sep 3, 2023
94ffe72
Merge pull request #50 from mmm999xp/R45
mmm999xp Sep 3, 2023
a2436e7
fix: getTweets order
mmm999xp Sep 3, 2023
388ab70
Merge pull request #51 from mmm999xp/R45
mmm999xp Sep 3, 2023
023999b
fix: err handler
mmm999xp Sep 3, 2023
12f947d
fix:get admin tweets createdAt
mmm999xp Sep 3, 2023
7ed3839
modify: get admin tweets
mmm999xp Sep 3, 2023
4a42fb7
modify: get admin users order
mmm999xp Sep 3, 2023
4b90878
fix:get admin users order
mmm999xp Sep 3, 2023
d43a38a
fix:get admin users order
mmm999xp Sep 3, 2023
29795aa
modify : getUserTweets add replycount likecount
mmm999xp Sep 3, 2023
2c34e61
Create README.md
mmm999xp Sep 3, 2023
7516975
FIX : Build ci (#52)
mmm999xp Sep 3, 2023
eb075b8
fix:createdAt time
keitakeiko Sep 3, 2023
cf5beb2
Merge branch 'main' of https://github.com/mmm999xp/twitter-api-2023
keitakeiko Sep 3, 2023
a7c7379
fix: admin get tweets createdAt
mmm999xp Sep 3, 2023
471deb5
fix: admin get tweets createdAt
mmm999xp Sep 3, 2023
e1f5bf5
fix: meeting user story
mmm999xp Sep 3, 2023
5a08252
fix env config
mmm999xp Sep 3, 2023
0224606
fix: admin don't use user function
mmm999xp Sep 4, 2023
7eac6d4
fix: dateformat and likecount and replycount
mmm999xp Sep 4, 2023
0e87ec1
fix: test
mmm999xp Sep 4, 2023
4f8c029
fix:test
mmm999xp Sep 4, 2023
1cc5400
fix:test
mmm999xp Sep 4, 2023
1c302ac
fix:test getUserReplies
mmm999xp Sep 4, 2023
903098f
fix: getUserLike and reply
mmm999xp Sep 4, 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
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
NODE_ENV=development
JWT_SECRET=
IMGUR_CLIENT_ID=
CLIENT_SECRET=
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules/*
/tests/*
12 changes: 12 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
env:
browser: true
commonjs: true
es2021: true
extends:
- standard
parserOptions:
ecmaVersion: 12
rules:
arrow-parens:
- warn
- as-needed
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
.DS_Store
*.DS_Store

node_modules/
temp/
upload/
note.js

# Logs
logs
*.log
Expand Down
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
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Simple Twitter | (前後分離組)

後端採用express開發API伺服器,前端使用react,以json格式互相傳遞資料,組成Simple Twitter的基本功能
# 功能
- 註冊/登入/登出
- 使用者要登入才能使用網站
- 使用者註冊重複/登入/登出失敗時,會跳出對應的錯誤提示
- 使用者
- 使用者能在首頁瀏覽所有的推文
- 使用者能回覆別人的推文
- 使用者點擊貼文方塊時,能查看該則貼文的詳情與回覆串
- 使用者可以追蹤/取消追蹤其他使用者
- 點擊貼文中使用者頭像時,能瀏覽該使用者的個人資料及推文
- 使用者可以在個人頁面編輯自己的名稱、介紹、大頭照和個人背景
- 使用者可以在設定頁面編輯自己的帳號、名稱、email和密碼
- 使用者能在首頁的右邊側邊欄,看見跟隨者數量排列前 10 的使用者推薦名單
- 後台管理
- 管理者可以瀏覽站內所有的使用者清單,依照推文數排序由多至少
- 管理者可以瀏覽全站的推文清單
- 管理者可以刪除任意推文

# 環境建置
- node.js 以及 npm
- npm,
- mySQL
- 任一種您喜歡或常用的資料庫GUI(如workbench)

# 安裝流程
1.開啟終端機將專案clone本機:
```
git clone https://github.com/mmm999xp/twitter-api-2023.git
```
2.進入存放此專案的資料夾
```
cd twitter-api-2023
```
3.環境變數設定
```
根目錄建立一個env檔案,根據.env.example檔案的輸入示範中輸入您自己的imgur金鑰﹑jwt secret等等
```
4.建立資料庫
開啟 MySQL workbench,再連線至本地資料庫,輸入以下建立資料庫

```
drop database if exists ac_twitter_workspace;
create database ac_twitter_workspace;

drop database if exists ac_twitter_workspace_test;
create database ac_twitter_workspace_test;
```
5.安裝 npm 套件, 將會自動安裝package.json的所有套件
```
npm install
```
6.db:migrate 設定
```
npx sequelize db:migrate
```
7.加入種子資料
```
npx sequelize db:seed:all
```
8.啟動專案
```
npm run dev
```
9.使用
終端機出現下列訊息: "http://localhost:3000"
可開啟瀏覽器輸入 http://localhost:3000 使用,會出現API server started!

10.預設使用者 Seed User
- 一般使用者帳號5組 (帳號:user1﹑user2﹑user3…etc,密碼皆為12345678)
- 管理者帳號有1組 (帳號:root ,密碼:12345678)
2 changes: 1 addition & 1 deletion _helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ function getUser(req) {
}

module.exports = {
getUser,
getUser
};
43 changes: 37 additions & 6 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,46 @@
if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'travis' && process.env.NODE_ENV !== 'ghactions') {
require('dotenv').config()
}
const express = require('express')
const helpers = require('./_helpers');

// const helpers = require('./_helpers')
// const flash = require('connect-flash')
const path = require('path')
const passport = require('./config/passport')
const session = require('express-session')
const app = express()
const port = 3000

const port = process.env.PORT || 3000
const apis = require('./routes')
const cors = require('cors')
const SESSION_SECRET = 'secret'
// use helpers.getUser(req) to replace req.user
/*
function authenticated(req, res, next){
// passport.authenticate('jwt', { ses...
};
*/

const corsOptions = {
origin: [
'http://localhost:3000',
'https://github.com/kotjy/ac-twitter',

'https://kotjy.github.io/ac-twitter',
'https://kotjy.github.io'
],
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
allowedHeaders: ['Content-Type', 'Authorization']
}

app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
app.use(cors())
app.use('/upload', express.static(path.join(__dirname, 'upload')))
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
app.use(session({ secret: SESSION_SECRET, resave: false, saveUninitialized: false }))
app.use(passport.initialize()) // 初始化 Passport
app.use(passport.session()) // 啟動Passport 存入session

app.use('/api', apis)
app.get('/', (req, res) => res.send('API server started!'))
// app.listen(port, () => console.log(`Example app listening on port ${port}!`))
app.listen(port, () => console.log(`http://localhost:${port}`))
module.exports = app
6 changes: 1 addition & 5 deletions config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@
"logging": false
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
"use_env_variable": "MYSQL_DATABASE_URL"
},
"travis": {
"username": "travis",
Expand Down
74 changes: 71 additions & 3 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,73 @@
const passport = require('passport')
const LocalStrategy = require('passport-local')
const passportJWT = require('passport-jwt')
const bcrypt = require('bcryptjs')
const { User } = require('../models')
const JWTStrategy = passportJWT.Strategy
const ExtractJWT = passportJWT.ExtractJwt
// 本地策略
passport.use(new LocalStrategy(
// customize user field
{
usernameField: 'account',
passwordField: 'password',
passReqToCallback: true
},
// authenticate user
(req, account, password, cb) => {
// 檢查請求路徑是否是前台或後台登入
const isUserLogin = req.path.includes('users')
const isAdminLogin = req.path.includes('admin')
User.findOne({ where: { account } })
// 如果找不到帳號
.then(user => {
if (!user) {
const err = new Error('帳號不存在!')
err.status = 401
throw err
}
if (isUserLogin && user.role !== 'user') {
const err = new Error('帳號不存在!')
err.status = 401
throw err
}
if (isAdminLogin && user.role !== 'admin') {
const err = new Error('帳號不存在!')
err.status = 401
throw err
}
bcrypt.compare(password, user.password).then(match => {
// 如果密碼錯誤也提示帳號不存在
if (!match) {
const err = new Error('帳號不存在!')
err.status = 401
throw err
}
return cb(null, user)
})
.catch(err => cb(err))
})
.catch(err => cb(err))
}
))
// JWT
const jwtOptions = {
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET || 'twitter',
passReqToCallback: true
}
// JWT
passport.use(
new JWTStrategy(jwtOptions, (req, jwtPayload, cb) => {
User.findByPk(jwtPayload.id, {
attributes: { exclude: ['password', 'createdAt', 'updatedAt'] }
})
.then(user => {
req.user = user
cb(null, user)
})
.catch(err => cb(err))
})
)



module.exports = passport
module.exports = passport
117 changes: 117 additions & 0 deletions controllers/admin-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
const jwt = require('jsonwebtoken')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc') // 引入 UTC 套件
const timezone = require('dayjs/plugin/timezone') // 引入時區套件
const helper = require('../_helpers')
const { User, Tweet, Like } = require('../models')
const datetimeHelper = require('../helpers/datetime-helper')
dayjs.extend(utc) // 使用 UTC 套件
dayjs.extend(timezone) // 使用時區套件

const adminController = {
signIn: (req, res, next) => {
try {
const userData = helper.getUser(req).toJSON()
delete userData.password // 刪除密碼
const token = jwt.sign(userData, process.env.JWT_SECRET, { expiresIn: '30d' }) // 簽發 JWT,效期為 30 天
res.status(200).json({
status: 'success',
message: `管理者${userData.account}已經成功登入!`,
data: {
token,
user: {
...userData,
updatedAt: dayjs(userData.updatedAt).tz('Asia/Taipei').format('YYYY-MM-DD HH:mm:ss'),
createdAt: dayjs(userData.createdAt).tz('Asia/Taipei').format('YYYY-MM-DD HH:mm:ss')
}
}
})
} catch (err) {
next(err)
}
},
getUsers: (req, res, next) => {
return User.findAll({
nest: true,
// raw: true, 因為使用raw時 sequelize無法正確取得關聯資料
attributes: ['id', 'account', 'name', 'avatar', 'banner'], // 不載入password
include: [
{ model: User, as: 'Followers', attributes: ['id'] },
{ model: User, as: 'Followings', attributes: ['id'] },
{ model: Tweet, attributes: ['id'] },
{ model: Like, attributes: ['id'] }
]
})
.then(users => {
users = users.map(user => {
return {
id: user.id,
account: user.account,
name: user.name,
avatar: user.avatar,
banner: user.banner,
tweetCounts: user.Tweets.length,
likeCounts: user.Likes.length,
followingCounts: user.Followings.length,
followerCounts: user.Followers.length
}
})
// 根據 tweetCounts 數字由多至少排序
users.sort((a, b) => b.tweetCounts - a.tweetCounts)
return users
})
.then(users => res.status(200).json(users))
.catch(err => next(err))
},
deleteTweet: (req, res, next) => {
const paramsTweetId = Number(req.params.id)
return Tweet.findByPk(paramsTweetId)
.then(tweet => {
if (!tweet) {
const err = new Error('推文不存在!')
err.status = 404
throw err
}
return tweet.destroy()
})
.then(deletedTweet => res.status(200).json({
status: 'success',
message: `id為 ${deletedTweet.id}的推文已被刪除!`
}))
.catch(err => next(err))
},
getTweets: (req, res, next) => {
return Tweet.findAll({
include: [
{ model: User, attributes: ['id', 'account', 'name', 'avatar', 'banner', 'createdAt'] }
],
next: true,
order: [['createdAt', 'DESC']]
})
.then(tweets => {
const resTweets = tweets.map(tweet => ({
id: tweet.id,
description: tweet.description,
UserId: tweet.userId,
createdAt: datetimeHelper.relativeTimeFromNow(tweet.createdAt),
User: {
id: tweet.User.id,
account: tweet.User.account,
name: tweet.User.name,
avatar: tweet.User.avatar,
banner: tweet.User.banner
},
Tweet: {
id: tweet.id,
description: tweet.description,
UserId: tweet.userId,
createdAt: datetimeHelper.relativeTimeFromNow(tweet.createdAt)
}
}))
return resTweets
})
.then(resTweets => res.status(200).json(resTweets))
.catch(err => next(err))
}
}
module.exports = adminController
Loading