Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
146 commits
Select commit Hold shift + click to select a range
bf3b002
feat: add eslint
Nilney Aug 22, 2023
8e71d4b
feat: add account and cover to user model
Nilney Aug 23, 2023
efae12a
feat: init models
Nilney Aug 23, 2023
cd2aef7
feat: add passport
Nilney Aug 23, 2023
e0ea72d
Merge pull request #1 from Nilney/feature/initProject
Harrison0502 Aug 24, 2023
90975cf
feat: add users seed
Harrison0502 Aug 24, 2023
ef6b744
feat: add tweets seed
Harrison0502 Aug 24, 2023
31e5ea8
feat: add replies seed
Harrison0502 Aug 24, 2023
1dbe497
feat: add api error handler
Harrison0502 Aug 24, 2023
1045c5b
Merge pull request #2 from Nilney/feature/addSeedData
Nilney Aug 24, 2023
7bbec93
Merge pull request #3 from Nilney/feature/addErrorHandler
Nilney Aug 24, 2023
6bef641
feat: add admin signin api
Nilney Aug 24, 2023
c51e0c0
feat:add user signin api
Harrison0502 Aug 24, 2023
25f1981
Merge pull request #4 from Nilney/feature/adminSignin
Harrison0502 Aug 24, 2023
972fd7a
fix: update signin route
Harrison0502 Aug 24, 2023
39c8fb3
Merge branch 'master' of https://github.com/Nilney/twitter-api-2020 i…
Harrison0502 Aug 24, 2023
dd68385
feat: install bcrypt module
Harrison0502 Aug 24, 2023
3d6c203
refactor: login-handler and route/index
Harrison0502 Aug 24, 2023
cc95437
feat: add admin getUsers api
Nilney Aug 25, 2023
59d3c32
feat: add admin getTweets api
Nilney Aug 25, 2023
94ddf10
refactor: switched to bcryptjs for password encryption
Harrison0502 Aug 25, 2023
9633656
Merge pull request #5 from Nilney/feature/signin
Nilney Aug 25, 2023
82f0b3e
Merge branch 'master' into feature/getAllUsers
Nilney Aug 25, 2023
0d9f143
Merge branch 'master' into feature/getAllTweets
Nilney Aug 25, 2023
c8cac3f
Merge branch 'feature/getAllUsers' into feature/getAllTweets
Nilney Aug 25, 2023
e366de0
feat: modify config for heroku
Harrison0502 Aug 25, 2023
0d97949
Merge pull request #8 from Nilney/feature/herokuDeploy
Nilney Aug 25, 2023
496e9c9
feat: add dayjs
Nilney Aug 26, 2023
abaa495
Merge pull request #7 from Nilney/feature/getAllTweets
Harrison0502 Aug 26, 2023
e9fc631
feat: add cors package
Harrison0502 Aug 26, 2023
3668e78
Merge pull request #9 from Nilney/feature/addCors
Nilney Aug 26, 2023
be491b7
feat: add default name to user seed data
Harrison0502 Aug 26, 2023
b1d221e
Merge pull request #10 from Nilney/feature/addSeedData
Nilney Aug 26, 2023
412dbd3
feat: add admin delete tweet api
Nilney Aug 26, 2023
a143c81
refactor: restructure the signin return data
Harrison0502 Aug 26, 2023
a2a6355
refactor: restructure return data of admin signin
Nilney Aug 26, 2023
0af8260
Merge pull request #11 from Nilney/feature/signin
Nilney Aug 26, 2023
dba44a5
Merge pull request #12 from Nilney/feature/adminSignin
Harrison0502 Aug 26, 2023
b3bda18
Merge remote-tracking branch 'origin' into feature/deleteTweet
Nilney Aug 26, 2023
763f804
feat: add authenticatedUser module
Nilney Aug 26, 2023
3d411a6
feat: add get all tweets api
Nilney Aug 26, 2023
86a1146
fix: return data without user password
Nilney Aug 26, 2023
f64e90a
Merge pull request #14 from Nilney/feature/getTweets
Harrison0502 Aug 26, 2023
6f1d2ef
Merge pull request #13 from Nilney/feature/deleteTweet
Harrison0502 Aug 26, 2023
f706f02
feat: add date time format module
Nilney Aug 26, 2023
5f52609
feat: add get tweet api
Nilney Aug 26, 2023
0d2ba53
Merge branch 'master' into feature/getTweet
Nilney Aug 26, 2023
ee5b28e
feat: add signup api
Harrison0502 Aug 26, 2023
50ae427
Merge branch 'master' of https://github.com/Nilney/twitter-api-2020 i…
Harrison0502 Aug 26, 2023
6266232
Merge branch 'master' of https://github.com/Nilney/twitter-api-2020 i…
Harrison0502 Aug 26, 2023
6c0b3b6
feat: add getUser api
Harrison0502 Aug 27, 2023
11be5d4
feat: modify getUser api and include passport data association
Harrison0502 Aug 27, 2023
801da63
Merge pull request #15 from Nilney/feature/getTweet
Harrison0502 Aug 27, 2023
0c888b7
feat:add user default role:'user'
Harrison0502 Aug 27, 2023
6e7c20d
refactor: use async/await for asynchronous operations
Harrison0502 Aug 27, 2023
4ee2535
Merge pull request #16 from Nilney/feature/signup
Nilney Aug 27, 2023
8903510
Merge branch 'master' into feature/getUser
Nilney Aug 27, 2023
4de53f4
Merge pull request #17 from Nilney/feature/getUser
Nilney Aug 27, 2023
ab8ef9c
feat: add post tweet api
Nilney Aug 27, 2023
9f0e967
feat: add post reply api
Nilney Aug 27, 2023
c558c8a
feat: add multer and imgur
Harrison0502 Aug 27, 2023
d72d13b
Merge pull request #18 from Nilney/feature/postTweet
Harrison0502 Aug 27, 2023
7361bee
chore: remove unnecessary console.log
Harrison0502 Aug 27, 2023
b97af96
Merge pull request #19 from Nilney/feature/postReply
Harrison0502 Aug 27, 2023
5b530b6
Merge branch 'master' of https://github.com/Nilney/twitter-api-2020 i…
Harrison0502 Aug 27, 2023
49ac7f5
feat: add user default avatar and cover
Harrison0502 Aug 27, 2023
c517ba6
Merge pull request #20 from Nilney/feature/editUser
Nilney Aug 27, 2023
fb2c4d3
Merge branch 'master' into feature/signup
Nilney Aug 27, 2023
13885ac
Merge pull request #21 from Nilney/feature/signup
Nilney Aug 27, 2023
27d5497
feat: add get tweet replies api
Nilney Aug 27, 2023
d400749
feat: add get user tweets api
Harrison0502 Aug 27, 2023
5d3c61b
feat: add isLiked property to identify whether a tweet is liked by user
Nilney Aug 27, 2023
017816e
Merge pull request #22 from Nilney/feature/getTweetReplies
Harrison0502 Aug 27, 2023
6b0e909
Merge branch 'master' of https://github.com/Nilney/twitter-api-2020 i…
Harrison0502 Aug 27, 2023
ae621d0
feat: add get user replies api
Harrison0502 Aug 27, 2023
3d335fd
feat: add get user likes api
Harrison0502 Aug 27, 2023
dfae8d8
feat:add get user followings api
Harrison0502 Aug 27, 2023
33ccb89
feat: add get user followers api
Harrison0502 Aug 27, 2023
1542f11
Merge pull request #23 from Nilney/feature/getTweets
Harrison0502 Aug 28, 2023
1d28639
feat: add user add like tweet api
Nilney Aug 28, 2023
88a3b6c
feat: add user remove like tweet api
Nilney Aug 28, 2023
fd98137
feat: add handling for cases with empty user data
Harrison0502 Aug 28, 2023
c652739
Merge pull request #30 from Nilney/feature/removeLike
Harrison0502 Aug 28, 2023
a5f62cd
Merge pull request #25 from Nilney/feature/getUserFollowers
Nilney Aug 28, 2023
ba3d850
fix: add order to admin get users api
Nilney Aug 28, 2023
d2e8964
Merge pull request #31 from Nilney/feature/getAllUsers
Harrison0502 Aug 28, 2023
f23b7ab
feat: add following api
Harrison0502 Aug 28, 2023
93c28de
feat: add delete following api
Harrison0502 Aug 28, 2023
3be4463
feat: add get top users api
Harrison0502 Aug 28, 2023
2340738
fix: getUserLikes message
Harrison0502 Aug 29, 2023
7a837c2
fix: getUserReplies and getUserLikes cannot read user.id
Harrison0502 Aug 29, 2023
c7dcf02
fix: add message when reply array is empty
Nilney Aug 29, 2023
b17782a
Merge pull request #35 from Nilney/feature/getTweetReplies
Harrison0502 Aug 29, 2023
198ab93
fix: add message when tweets array is empty
Nilney Aug 29, 2023
d87b5f7
refactor: modify the way to get current user's followings
Harrison0502 Aug 29, 2023
e235e19
fix: add message when tweets array is empty
Nilney Aug 29, 2023
90fa3c2
feat: add returning of following user value
Harrison0502 Aug 29, 2023
9e1ac8d
Merge pull request #37 from Nilney/feature/getAllTweets
Harrison0502 Aug 29, 2023
96fb1f7
Merge pull request #36 from Nilney/feature/getTweets
Harrison0502 Aug 29, 2023
336517e
Merge pull request #33 from Nilney/feature/deleteFollowing
Nilney Aug 29, 2023
1326996
Merge pull request #34 from Nilney/feature/getTopUsers
Nilney Aug 29, 2023
b4b05dd
style: remove unnecessary content
Nilney Aug 29, 2023
582aef3
Merge pull request #38 from Nilney/bugfix/global
Harrison0502 Aug 30, 2023
1c58f4d
feat: add introduction and name judge && add default avatar and cover
Harrison0502 Aug 30, 2023
11fee6b
feat: add judge account only contain characters and number
Harrison0502 Aug 30, 2023
defe161
fix: add tweet createAt relative time
Nilney Aug 30, 2023
fc5a79b
Merge pull request #39 from Nilney/feature/editUser
Nilney Aug 30, 2023
868e453
Merge pull request #40 from Nilney/feature/getTweet
Harrison0502 Aug 30, 2023
2ff3502
feat: add prevent following admin and avoid returning user's password
Harrison0502 Aug 30, 2023
ff032b1
Merge pull request #41 from Nilney/feature/deleteFollowing
Nilney Aug 30, 2023
0dee04b
fix: modify get top user's follower count
Harrison0502 Aug 30, 2023
17de30b
Merge pull request #42 from Nilney/feature/getTopUsers
Nilney Aug 30, 2023
1cfd68b
fix: modify edit user
Harrison0502 Aug 31, 2023
34df04e
fix: editUser password length to confirm to test
Harrison0502 Aug 31, 2023
80d4fd8
Merge pull request #43 from Nilney/feature/editUser
Nilney Aug 31, 2023
cec068e
fix: modify editUser's currentUserId type
Harrison0502 Aug 31, 2023
355d8dc
Merge pull request #44 from Nilney/feature/editUser
Nilney Aug 31, 2023
a30cbbf
fix: modify getUserTweet's tweetCreatedAt time & return user's avatar…
Harrison0502 Aug 31, 2023
c79eeab
fix: delete user's password form return
Harrison0502 Aug 31, 2023
5b990e8
Merge pull request #45 from Nilney/feature/getUserTweets
Nilney Aug 31, 2023
7f98c33
Merge pull request #46 from Nilney/feature/deleteFollowing
Nilney Aug 31, 2023
f43ac1a
fix: modify currentUserFollowing
Harrison0502 Aug 31, 2023
efca450
Merge pull request #47 from Nilney/feature/getTopUsers
Nilney Aug 31, 2023
41d7e27
feat: add isLiked & current likedAmount to return data
Nilney Aug 31, 2023
59a94ea
Merge pull request #48 from Nilney/feature/addLike
Harrison0502 Aug 31, 2023
ba24114
doc: add README
Nilney Aug 31, 2023
0e575f0
doc: add email of test account to README
Nilney Aug 31, 2023
e3c460e
fix: add default value to JWTSecret
Nilney Aug 31, 2023
c645cf0
Merge branch 'master' into bugfix/global
Nilney Aug 31, 2023
d9942cf
fix: add JWT_SECRET to travis env
Nilney Aug 31, 2023
1a7a9cd
Merge pull request #50 from Nilney/bugfix/global
Harrison0502 Sep 1, 2023
588cafa
fix: add Node.js version
Nilney Sep 1, 2023
bf25d4f
fix: return error when tweet description only with space
Nilney Sep 1, 2023
38e41d0
Merge pull request #49 from Nilney/doc/readme
Harrison0502 Sep 1, 2023
cb665f9
Merge pull request #51 from Nilney/feature/postTweet
Harrison0502 Sep 1, 2023
189d43b
fix: return error when comment only contain space
Nilney Sep 1, 2023
1a3929b
fix: resolve cannot read property 'length' error in editUser
Harrison0502 Sep 1, 2023
2617c36
Merge branch 'master' of https://github.com/Nilney/twitter-api-2020 i…
Harrison0502 Sep 1, 2023
0898193
Merge pull request #52 from Nilney/feature/postReply
Harrison0502 Sep 1, 2023
5c0352f
Merge pull request #53 from Nilney/feature/editUser
Nilney Sep 1, 2023
e8fc591
fix: add 'isFollowed' return value and update user.role condition
Harrison0502 Sep 3, 2023
798d73a
Merge branch 'master' of https://github.com/Nilney/twitter-api-2020 i…
Harrison0502 Sep 3, 2023
36c1713
fix: return 'isFollowed' nested under 'user'
Harrison0502 Sep 3, 2023
4af0027
Merge pull request #54 from Nilney/feature/deleteFollowing
Nilney Sep 3, 2023
7cb02fc
fix: add avatar/cover if avatarPath/coverPath doesn't exist
Harrison0502 Sep 3, 2023
4e5045a
Merge pull request #55 from Nilney/feature/editUser
Nilney Sep 3, 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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
JWT_SECRET=
IMGUR_CLIENT_ID=
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules/*
/test/*
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,8 @@ typings/
.fusebox/

# DynamoDB Local files
.dynamodb/
.dynamodb/

# Image files
temp/
upload/
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:

# 設定參數(Travis CI 會按照參數分別執行)
env:
- NODE_ENV=travis
- NODE_ENV=travis JWT_SECRET=alphacamp

# 在 install 前執行的指令
before_install:
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
126 changes: 126 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Simple Twitter API

## 專案介紹
本專案以Node.js及express框架製作,搭配MySQL關聯式資料庫,提供Simple Twitter後端RESTful API。


## 主要功能

### 前台(使用者)
- 註冊及登入功能
- 瀏覽推文及回覆
- 發佈推文及回覆推文
- 跟隨和取消跟隨其他使用者
- 喜歡及取消喜歡推文功能
- 編輯自己的帳號與密碼等相關資訊
- 編輯自己的公開個人檔案
- 瀏覽他人頁面及其推文、回覆、喜歡的內容
- 瀏覽推薦跟隨的名單

### 後台(管理者)
- 瀏覽全站使用者列表
- 瀏覽全站使用者發佈的推文數量及推文受喜歡的數量
- 瀏覽全站使用者跟隨數中的人數及被多少人跟隨
- 瀏覽及刪除全站推文


## 測試 / 種子帳號
| Role | Account | Email | Password |
| :-------:| :-------: | :---------------: | :------: |
| 前台使用者 | user1 | [email protected] | 12345678 |
| 後台管理者 | root | [email protected] | 12345678 |


## 安裝及使用

### 本地基礎設置
確認本地端已安裝 Node.js 、 npm 、 Git 、 MySQL Workbench

### 資料庫連線設定
您可選擇本地資料庫或遠端資料庫作為您的 Database,使用 MySQL Workbench 在應用程式首頁建立一個新的 MySQL Connections,設定您資料庫的相關參數並建立連線:
- 伺服器位址(Host)
- 埠號(Port)
- 資料庫名稱(Database Name)
- 使用者名稱(Username)
- 密碼(Password)

### 開始
1. 打開terminal,輸入以下指令Clone本專案至本地
```
$ git clone https://github.com/Nilney/twitter-api-2020.git
```

2. 進入此專案資料夾
```
$ cd twitter-api-2020
```

3. 安裝本專案相依套件
```
$ npm install
```

4. 參考 `.env.example` 建立 `.env` 文件,並設置您的環境變數
```
JWT_SECRET=<setting by your own>
IMGUR_CLIENT_ID=<you imgur client id>
```

5. 於 `config/config.json` 文件中設定您資料庫的相關訊息

6. 在資料庫內建立資料表
```
$ npx sequelize db:migrate
```

7. 建立種子資料
```
$ npx sequelize db:seed:all
```

8. 輸入以下指令,快速啟動本專案
```
$ npm run start
```
- 種子資料含以下資料
- 1 組管理者帳號、5 組一般使用者帳號
- 每個使用者擁有 10 篇推文
- 每篇推文底下有隨機 3 則留言

9. 於終端機見提示訊息\
**Example app listening on port 3000!**\
代表您已成功啟動本專案

10. 若要停止使用,請輸入以下指令
```
ctrl + c
```


## API 文件
專案 [Postman連結](https://documenter.getpostman.com/view/29223126/2s9Y5Ty4mq#53591c9b-0bc5-40bb-b0f8-b63b87c41ebb)


## 開發工具
包含以下但不限於
- Node.js @14.16.0
- Express @4.16.4
- mysql2 @1.6.4
- sequelize: @6.18.0
- sequelize-cli @5.5.0
- passport @0.4.0
- passport-jwt @4.0.0
- method-override @3.0.0
- imgur @1.0.2
- multer @1.4.3
- bcryptjs @2.4.3
- dayjs @1.10.6
- cors @2.8.5
- dotenv @10.0.0
- jsonwebtoken @8.5.1\
*其餘詳見檔案 package.json*


## 開發人員
[Harrison Chen](https://github.com/Harrison0502)\
[Lynn Lin](https://github.com/Nilney)
8 changes: 4 additions & 4 deletions _helpers.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

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

module.exports = {
getUser,
};
getUser
}
21 changes: 14 additions & 7 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config()
}

const express = require('express')
const helpers = require('./_helpers');
const passport = require('passport')
const router = require('./routes')
const cors = require('cors')

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

// use helpers.getUser(req) to replace req.user
function authenticated(req, res, next){
// passport.authenticate('jwt', { ses...
};
app.use(cors())
app.use(passport.initialize())
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
app.use('/api', router)

app.get('/', (req, res) => res.send('Hello World!'))
app.get('/', (req, res) => res.send('Hello Alphitter!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

module.exports = app
1 change: 1 addition & 0 deletions config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"logging": false
},
"production": {
"use_env_variable": "MYSQL_DATABASE_URL",
"username": "root",
"password": null,
"database": "database_production",
Expand Down
25 changes: 24 additions & 1 deletion config/passport.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
const passport = require('passport')
const passportJWT = require('passport-jwt')
const { User } = require('../models')

const JWTStrategy = passportJWT.Strategy
const ExtractJWT = passportJWT.ExtractJwt

const jwtOptions = {
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET,
passReqToCallback: true
}

module.exports = passport
passport.use(new JWTStrategy(jwtOptions, async (req, jwtPayload, cb) => {
try {
const user = await User.findByPk(jwtPayload.id, {
include: [
{ model: User, as: 'Followings' }
]
})
req.user = user
cb(null, user)
} catch (err) {
cb(err)
}
}))

module.exports = passport
111 changes: 111 additions & 0 deletions controllers/admin-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
const jwt = require('jsonwebtoken')
const sequelize = require('sequelize')
const { getUser } = require('../_helpers')
const { User, Tweet, Reply, Like } = require('../models')
const { relativeTimeFromNow } = require('../helpers/dayjs-helpers')

const adminController = {
signIn: (req, res, next) => {
try {
const userData = getUser(req).toJSON()
delete userData.password
const token = jwt.sign(userData, process.env.JWT_SECRET, { expiresIn: '30d' })

res.json({
token,
user: userData
})
} catch (err) {
next(err)
}
},
getUsers: async (req, res, next) => {
try {
const users = await User.findAll({
attributes: [
'id',
'name',
'account',
'avatar',
'cover',
'role',
[
sequelize.literal('(SELECT COUNT(*) FROM Tweets WHERE Tweets.UserId = User.id)'), 'tweetsAmount'
],
[
sequelize.literal('(SELECT COUNT(*) FROM Likes WHERE Likes.TweetId IN (SELECT id FROM Tweets WHERE Tweets.UserId = User.id))'), 'likedAmount'
],
[
sequelize.literal('(SELECT COUNT(*) FROM Followships WHERE Followships.followerId = User.id)'), 'followingAmount'
],
[
sequelize.literal('(SELECT COUNT(*) FROM Followships WHERE Followships.followingId = User.id)'), 'followerAmount'
]
],
order: [
[sequelize.literal('tweetsAmount DESC')], // 依推文數量降冪排序
['role', 'DESC'] // admin排最後
],
raw: true
})

res.status(200).json(users)
} catch (err) {
next(err)
}
},
getTweets: async (req, res, next) => {
try {
const tweets = await Tweet.findAll({
include: [
{
model: User,
attributes: ['id', 'account', 'name', 'avatar']
}
],
order: [['createdAt', 'DESC']],
raw: true,
nest: true
})

if (!tweets.length) {
return res.status(200).json({
status: 'success',
message: '目前沒有任何推文。'
})
}

// 擷取推文訊息50字
const data = tweets.map(tweet => ({
...tweet,
description: tweet.description.substring(0, 50),
createdAt: relativeTimeFromNow(tweet.createdAt)
}))

res.status(200).json(data)
} catch (err) {
next(err)
}
},
deleteTweet: async (req, res, next) => {
try {
const tweet = await Tweet.findByPk(req.params.id)
if (!tweet) throw new Error('此推文不存在!')

// 刪除推文時連同該則推文底下的留言及喜歡都一起刪除
await tweet.destroy()
await Reply.destroy({ where: { TweetId: tweet.id } })
await Like.destroy({ where: { TweetId: tweet.id } })

res.status(200).json({
status: 'success',
message: '成功刪除推文!',
tweet
})
} catch (err) {
next(err)
}
}
}

module.exports = adminController
Loading