---
# System prepended metadata

title: 9.2帳密登陸管理+存檔案

---

---
# 9.2帳密登陸管理+存檔案
---
# 9.2帳密登陸管理+存檔案

## 將密碼加密後=>儲存/供登陸比對
## 註冊
routes
```javascript=
import express from 'express'
import { register } from '../controller/user.js'

const router = express.Router()

router.post('/', register)

export default router

```
controller
```javascript=
import users from '../models/users.js'
import bcrypt from 'bcrypt'

export const register = async (req, res) => {
  try {
// 略過驗證密碼長度/帳號重複等
    req.body.password = bcrypt.hashSync(req.body.password, 10)
    await users.create(req.body)
    res.status(200).json({ success: true, message: '' })
}

```

---
## 登陸
routes
```javascript=
// 之前內容略過
import { login } from '../controller/user.js'
import * as auth from '../middleware/auth.js'

router.post('/login', auth.login, login)



```
passport
```javascript=
// 之前內容略過
import passport from 'passport'
import bcrypt from 'bcrypt'
import passportLocal from 'passport-local'
import passportJWT from 'passport-jwt'
import users from '../models/users.js'

const LocalStrategy = passportLocal.Strategy

// bcrypt驗證帳秘(供middleware匯入用)

passport.use('login', new LocalStrategy({
  usernameField: 'username',
  passwordField: 'password'
},
async (username, password, done) => {
  try {
    const user = await users.findOne({ username })
    if (!user) {
      return done(null, false, { message: '無該帳號' })
    }
    if (!bcrypt.compareSync(password, user.password)) {
      return done(null, false, { message: '密碼錯誤' })
    }
    return done(null, user)
  } catch (err) {
    return done(err, false)
  }
}
))

```
middleware
```javascript=
// 無使用到的略過
import passport from 'passport'
import '../passport/passport.js'

// passport 驗證過後，才req.user = user 並下一步，不然報錯
export const login = (req, res, next) => {
  passport.authenticate('login', { session: false }, (err, user, info) => {
    if (err) {
      res.status(400).json({ success: false, message: err })
      return
    }
    if (!user) {
      res.status(400).json({ success: false, message: info.message })
      return
    }
    req.user = user
    next()
  })(req, res, next)
}

```



 controller

.env 新增 
JWT_SECRET=隨便打一段密鑰
```javascript=
// 無使用到的略過
import bcrypt from 'bcrypt'
import jwt from 'jsonwebtoken'


export const login = async (req, res) => {
// 把id變成不易讀token (內容,加密鑰,過期時間)
const token = jwt.sign({ _id: req.user._id.toString() }, process.env.JWT_SECRET, { expiresIn: '1h' })
req.user.tokens.push(token)
await req.user.save()
res.status(200).json({ success: true, message: '登陸成功', token })
  
}
```

---
## 讀取
routes
```javascript=
// 之前內容略過
import { getData } from '../controller/user.js'
import * as auth from '../middleware/auth.js'

router.get('/me', auth.jwt, getData)



```
passport
```javascript=
// 之前內容略過
import passport from 'passport'
import passportJWT from 'passport-jwt'
import users from '../models/users.js'

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

// passport 使用JWTStrategy 
// 讀取Bearer的token+ 使用JWT_SECRET解碼 +紀錄Req供使用
//

passport.use('jwt', new JWTStrategy({
  jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
  secretOrKey: process.env.JWT_SECRET,
  passReqToCallback: true
},
async (req, payload, done) => {
//     拆解出token 跟使用者檢查是否相符/未過期
  const token = req.headers.authorization.split(' ')[1]
  try {
    const user = await users.findOne({ _id: payload._id, tokens: token })
    if (user) {
      return done(null, { user, token })
    }
    return done(null, false, { message: '請重新登錄' })
  } catch (err) {
    return done(err, false)
  }
}
))
```
middleware
```javascript=
// 無使用到的略過
import passport from 'passport'
import jsonwebtoken from 'jsonwebtoken'
import '../passport/passport.js'

export const jwt = (req, res, next) => {
  passport.authenticate('jwt', { session: false }, (err, data, info) => {
    if (err) {
      res.status(400).json({ success: false, message: err })
      return
    }
    if (!data) {
      if (info instanceof jsonwebtoken.JsonWebTokenError) {
        res.status(400).json({ success: false, message: 'JWT錯誤' })
      } else {
        res.status(400).json({ success: false, message: info.message })
      }
      return
    }
    req.user = data.user
    req.token = data.token
    next()
  })(req, res, next)
}
```



 controller
```javascript=
// 無使用到的略過

export const getData = async (req, res) => {
  res.status(200).json({
    success: true,
    message: '',
    result: {
      _id: req.user._id,
      username: req.user.username,
      avatar: req.user.avatar
    }
  })
}
```




---

## 登出
routes
```javascript=
// 之前內容略過
import {  logout } from '../controller/user.js'

router.delete('/logout', auth.jwt, logout)

```

 controller
```javascript=
// 無使用到的略過
export const logout = async (req, res) => {
  try {
    req.user.tokens = req.user.tokens.filter(tk => tk !== req.token)
    await req.user.save()
    res.status(200).json({ success: true, message: '登出成功' })
  } catch (err) {
    res.status(500).json({ success: false, message: '伺服器錯誤' })
  }
}
```

---

# 新增頭貼


## 辦帳號

雲端存檔案平台=>註冊
記得要改成下方的選單 不然要重辦帳號
![](https://i.imgur.com/qNm0Zpl.png)


npm i  bcrypt(加密.?) passport(驗證登錄套件 ex:discord line 等) passport-jwt passport-local



![](https://i.imgur.com/Kc0em1L.png)
![](https://i.imgur.com/h2JlrCz.png)

![](https://i.imgur.com/viRse0e.png)

---

## 上傳檔案


npm i multer(處理上傳檔案) cloudinary() multer-storage-cloudinary(處理後再傳到雲端)


routes
```javascript=
// 之前內容略過

import { editAvatar } from '../controller/user.js'
import upload from '../middleware/upload.js'

router.patch('/avatar', auth.jwt, upload, editAvatar)


```
middleware => upload.js

.env 新增 
CLOUDINARY_NAME=
CLOUDINARY_KEY=
CLOUDINARY_SECRET=
```javascript=
// 無使用到的略過
import multer from 'multer'
import { v2 as cloudinary } from 'cloudinary'
import { CloudinaryStorage } from 'multer-storage-cloudinary'

// 帳密供登陸cloudinary
cloudinary.config({
  cloud_name: process.env.CLOUDINARY_NAME,
  api_key: process.env.CLOUDINARY_KEY,
  api_secret: process.env.CLOUDINARY_SECRET
})

// 定義 上傳方式/克制限制/基本限制
const upload = multer({
  storage: new CloudinaryStorage({ cloudinary }),
  fileFilter (req, file, callback) {
    if (!file.mimetype.includes('image')) {
      callback(new multer.MulterError('LIMIT_FORMAT'), false)
    } else {
      callback(null, true)
    }
  },
  limits: {
    fileSize: 1024 * 1024
  }
})

// 如果有err => 是multer的就是上船相關(自定義文字)/伺服器=>正常上傳下一步
export default async (req, res, next) => {
  upload.single('image')(req, res, err => {
    if (err instanceof multer.MulterError) {
      let message = '上傳錯誤'
      if (err.code === 'LIMIT_FORMAT') {
        message = '檔案格式錯誤'
      } else if (err.code === 'LIMIT_FILE_SIZE') {
        message = '檔案太大'
      }
      res.status(400).json({ success: false, message })
    } else if (err) {
      res.status(500).json({ success: false, message: '伺服器錯誤' })
    } else {
      next()
    }
  })
}


```



 controller

```javascript=
// 無使用到的略過
export const editAvatar = async (req, res) => {
  try {
//       把上船的檔案路徑,加到該用戶內
    req.user.avatar = req.file.path
    await req.user.save()
    res.status(200).json({ success: true, message: '上傳成功' })
  } catch (err) {
    res.status(500).json({ success: false, message: '伺服器錯誤' })
  }
}
```

讀FORM-DATA 靠下方 MULTER
![](https://i.imgur.com/seUFu4F.png)
![](https://i.imgur.com/iKwhtyo.png)


