Edit Profile, seeProfile

김종민·2022년 10월 31일
0

Youtube

목록 보기
11/23

EditProfile
다른 부분은 크게 문재될게 없으나
password edit할 경우 다시
hash화 시켜야 되므로 password change부분에 집중해서 봐준다.


1. userRouter.js

import express from 'express'
import {
  finishGithubLogin,
  getChangePassword,
  getUserEdit,
  logout,
  postChangePassword,
  postUserEdit,
  seeUser,
  startGithubLogin,
} from '../controllers/userController'
import {
  avatarUpload,
  protectorMiddleware,
  publicOnlyMiddleware,
} from '../middlewares'

const userRouter = express.Router()

userRouter.get('/:id([0-9a-f]{24})', seeUser)
///매우중요, 여기서 error나면, 아래부분 다 안먹힘.
///mongo로 만들어지는 id는 위와같이 hexa?화 시켜주어야함.
///user의 profile보는 부분
///seeUser controller부분으로 이어짐.

userRouter
  .route('/edit')
  .get(getUserEdit)
  .all(protectorMiddleware)
  .post(avatarUpload.single('avatar'), postUserEdit)
///protectorMiddleware와 avatarUpload는 다음 POST에서다룸.
///'/edit'path에 get, post두개를 만들고,
/// userController.js에 getUserEdit, postUserEdit 
/// 두개의 함수를 만든다
///password change는 복잡해서 아래에 다른 path로 만듬.

userRouter.get('/logout', logout)
userRouter.get('/github/start', publicOnlyMiddleware, startGithubLogin)
userRouter.get('/github/finish', publicOnlyMiddleware, finishGithubLogin)

userRouter
  .route('/change-password')
  .all(protectorMiddleware)
  .get(getChangePassword)
  .post(postChangePassword)
///change-password부분은 따로 path를 만들고
///get, post부분의 controller를 userController에 만듬.

export default userRouter

2. useController.js

import User from '../models/User'
import bcrypt from 'bcrypt'
import fetch from 'node-fetch'
import Video from '../models/Video'
//front에서는 fetch사용가능하나, server에서는 불가능
//그래서 server에서 fetch사용 위해 import함.


export const seeUser = async (req, res) => {
  const { id } = req.params
  const user = await User.findById(id).populate('videos')
  console.log(user)
  if (!user) {
    return res.status(400).render('404', { pageTitle: 'User not Found' })
  }
  // const videos = await Video.find({ owner: user._id })
  // populate를 사용하지 않을경우, videos를 load하고 
  // pug파일에 넘겨줘야한다.
  return res.render('profile', {
    pageTitle: `${user.email} Profile`,
    user,
  })
}
///user의 profile을 보는부분,req.params로 user의 id를 받아서
/// User DB에서 id유저를 찾음.
///populate가 매우 중요한데, userSchema에서
///videos: [
    { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'Video' },
  ],
///위와 같이 videos로 Video를 ref했기 때문에, 
///populate를 사용해서 user와 ref된 video를 다 같이 불러줌.
///그래서 profile.pug 파일에 user만 넘겨주면 ref된 videos도
///같이 넘어감.

export const getUserEdit = (req, res) => {
  res.render('edit-profile', { pageTitle: 'Edit Profile' })
}
///edit path로 req가 오면, edit-profile.pug파일을 
///rendering해 줌.

/// '/edit' path에서의 POST처리('edit-profile.pug')
export const postUserEdit = async (req, res) => {
  const {
    session: {
      user: { _id, avatarUrl, email: sessionEmail, username: sessionUsername },
    },
    ///req.session.user에서 loggedIn한 user의 _id, email,
    ///avatarUrl, username등을 받아옴.
    ///'edit-profile.pug'파일에서 받은 아름과 중복을 피하기 위해
    ///email, username을 renam한다.
    
    body: { name, email, username, location },
    file,
    ///'edit-profile.pug' 에서 입력한 값들을 req.body로 받아옴.
    ///file은 나중에 다루겠지만, req.file로 받음.
  } = req
  console.log(file)

  let searchParam = []
  ///searchParam이라는 빈 배열을 하나 만듬.
  
  if (sessionEmail !== email) {
    searchParam.push({ email })
  }
  ///loggedIn된 user의 email값과 'edit-profile.pug'에서 입력된
 ///email 값이 다른 경우에만, searchParam에 담는다. 
  
  if (sessionUsername !== username) {
    searchParam.push({ username })
  }
 ///loggedIn된 user의 username값과 'edit-profile.pug'에서 입력된
 ///username값이 다른 경우에만, searchParam에 담는다. 
  
  ///searchParams에 뭔가가 담겼을때만, 아래부분을 실행.
  if (searchParam.length > 0) {
    const foundUser = await User.findOne({ $or: searchParam })
    ///searchParam에 담긴 값으로 user를 찾는다.
    if (foundUser && String(foundUser._id) !== String(_id)) {
      return res.status(400).render('edit-profile', {
        pageTitle: 'Edit Profile',
        errorMessage: 'This username/email is already taken',
      })
    }
  }
  ///User를 찾았고, 찾은 User의 id와 로그인되어 있는 user의
  ///id가 다른 경우 error를 날린다.
  ///즉, 무슨말이냐면, req.body로 받은 username이나 email값으로
  ///user를 찾는다는 뜻은, 바꾸려는 email이나, username이 존재
  ///하므로, 그 username이나 email은 사용할 수 없다는 뜻임.
  ///username과 email은 userSchema에서 unique로 설정했음.
  
  const updateUser = await User.findByIdAndUpdate(
    _id,
    {
      avatarUrl: file ? file.path : avatarUrl,
      name,
      username,
      email,
      location,
    },
    { new: true }
  )
  ///위에서 발생할 수 있는 error들을 defensive했으면, 
  ///findByIdAndUpdate로 user의 profile을 update한다.
  ///file은 DB에 path만 입력하는데,
  ///path는 req.file의 req.file.path에 존재한다.
  ///update일때는 {new: true}를 넣어준다.
  ///아래는 session에 하나하나 바뀐 data를 넣어주는 코딩
  ///그 아래는 updateUser 하나로 req.session.user에 
  ///바로 넣어주는 코딩
  
  // req.session.user = {
  //   ...req.session.user, ==>바뀐게없는 부분은 기존걸쓴다는뜻.
  //   name,
  //   email,
  //   username,
  //   location,
  // }
  req.session.user = updateUser
  return res.redirect('/users/edit')
  ///update가 끝났으면, 위 path로 redirect한다.
}

export const getChangePassword = (req, res) => {
  if (req.session.user.socialOnly === true) {
    return res.redirect('/')
  }
  ///socialOnly === true는 git으로 login했다면, password
  ///가 없으므로, 바로 home으로 redirect한다.
  
  return res.render('change-password', { pageTitle: 'Change Password' })
}
///change-password.pug를 rendering함.

export const postChangePassword = async (req, res) => {
  const {
    session: {
      user: { _id, password },
    },
    ///session으로 부터 loggedInUser의 id와 password 받음
    body: { oldPassword, newPassword, newPasswordConfirm },
    ///'change-password.pug'에서 입력된 값을 받음.
  } = req
  const ok = await bcrypt.compare(oldPassword, password)
  ///session의 password값과 'change-password'에서 입력된
  ///oldPassword 값이 같을때만, 아래로 진행시킴.
  
  if (!ok) {
    return res.status(400).render('change-password', {
      pageTitle: 'Change Password',
      errorMessage: 'The OldPassword incorrect.',
    })
  }
  ///loggedInUser의 password와 'change-password'에서 입력된
  ///password 값이 다를 경우, errorMessage날림.
  
  if (newPassword !== newPasswordConfirm) {
    return res.status(400).render('change-password', {
      pageTitle: 'Change Password',
      errorMessage: 'The NewPassword does not match.',
    })
  }
  ///바꿀려는 두개의 password값이 다를 경우 errorMessage날림.
  
  const user = await User.findById(_id)
  ///session의 id값으로 user를 찾음.
  
  user.password = newPassword
  ///찾은 user의 password를 pug파일에서 받은 newPassoword로 바꿈.
  
  await user.save()
  ///passoword를 바꿨으면, user를 save함.
  
  req.session.user.password = user.password
  ///session의 password를 바꾼 password로 바꿔줌.
  
  return res.redirect('/users/logout')
  ///모든 과정이 끝이 났으면, logout시킴.
}

NOTICE!!! session에 다시 입력시키는 이유는 session은 DB가 아닌
memory에 write되는 것이라, DB를 update(edit)시켜도,
로그인시 쓰여진 session 그대로 유지된다.
그래서 로그인을 다시 하는 경우가 아니라면, session에도
바뀐 data를 다시 write 해주어야 한다.

3. edit-profile.pug

extends base.pug

block content 
    h1 Edit Profile
    if errorMessage 
        h3=errorMessage
    img(src='/'+loggedInUser.avatarUrl, width="100", height="100")
    ///image를 불러주는 html,loggedInUser는 session에
    ///저장되어 있는 정보임.
    
    form(method="POST", enctype='multipart/form-data')
    ///file을 upload할때는 반드시 enctype='mu~~~~'을
    ///설정해 주어야 함.
        label(for='avatar') Avatar
        input(type="file", id='avatar', name="avatar", accept='image/*')
///다음 POST에 다루겠지만, label에는 for, 밑이는 id로
///label이랑, input을 연결함. accept는 모든 image파일이라는뜻.
        
///edit에서 default값은 session에 담겨있는 loggedInUser의
///값들을 default값으로 해 준다.(value에)
        input(name="email", placeholder="Email", type="email",value=loggedInUser.email )
        input(name="username", placeholder="Username", type="text", value=loggedInUser.username)
        input(name="name", placeholder="Name", type="text", value=loggedInUser.name)
        input(name="location", placeholder="Location", type="text",value=loggedInUser.location )
        input( type="submit", value="Update Profile")
        if !loggedInUser.socialOnly
            hr
            a(href="change-password") Change Password →
///change-password부분은 따로 page를 만들어준다.
            

4. change-password.pug

extends base.pug

block content 
 h3=pageTitle
 if errorMessage 
     span=errorMessage
 form(method="POST")
     input(placeholder="Currnet Password",name="oldPassword", type="password")
     ///기존 사용중인 password입력란.
     
     input(placeholder="New Password", name="newPassword", type="password")
     ///바꾸려는 password입력란.
     
     input(placeholder="Confirm Password", name="newPasswordConfirm", type="password")
     ///바꾸려는 password 한번더 확인하기.
     
     input(type="Submit" value="Change Password")

5. profile.pug

extends base.pug
include mixins/video

block content 
  each video in user.videos 
      +video(video)
  else 
      li Sorry nothing found.

mixins폴더의 video를 불러서 data를map 한다.
populate를 써서, user data에 videos를 같이 보내줬음.

6. mixins/video.pug

mixin video(info)
    div 
        h4
            a(href=`/videos/${info.id}`)=info.title
        video(src='/'+info.fileUrl, controls, width=300, height=200)  
        ///video를 부를때는 앞에 '/'를 붙여주고, 
        ///controls를 넣어준다.
        p=info.description
        each hashtag in info.hashtags 
            li=hashtag
        small=info.createdAt
        hr  
            
profile
코딩하는초딩쌤

0개의 댓글