nodebird express server

Donghun Seol·2022년 10월 14일
0

node.js 교과서

목록 보기
10/12

setup packages

1. init project with npm

npm init

2. install sequelize packages

npm i sequelize sequelize-cli mysql2
npx sequelize init

3. make necessary file and dirs

mkdir views routes public passport
touch .env

4. install express middlewares

npm i express cookie-parser express-session morgan multer dotenv nunjucks
npm i -D nodemon

setup base files

1. app.js

require packages, middlewares
dotenv config
configure template engine
configure session and cookie
connect db

configure default router
general error handling router with error rendering template

2. views

copy from textbook repo

3. .env

define current environment
cookie-secret
port number

define DB models

1. user.js

static associate(db) { // db object contains other models including this
    db.User.hasMany(db.Post);

    db.User.belongsToMany(db.User, { //user gets followingId as Follwers
      foreignKey: 'followingId',
      as: 'Followers',
      through: 'Follow',
    });

    db.User.belongsToMany(db.User, { // user gets followerId as Followings
      foreignKey: 'followerId',
      as: 'Followings',
      through: 'Follow',
    });
  }

2. post.js

3. hashtag.js

4. index.js

db connection setup and create

1. config.json

configure id, password, host, etc..

2. create db with sequelize-cli

$ npx sequelize db:create

3. edit app.js

const { sequelize } = require('./models');

sequelize.sync({ force: false })
  .then((result) => {
  	console.log(result.options.host, result.options.database, result.options.username);
  	console.log('DB connection successful')
	})
  .catch((err) => console.error(err, 'DB connection failed'));
  1. run app.js to create tables according to models and DEBUG!!

implement login features with passport

1. install packages

$ npm i passport passport-local passport-kakao bcrypt

2. require passport module and configure at app.js

// app.js
const passport = require('passport');
const passportConfig = require('./passport');

const app = express();
passportConfig();

app.use(session(/* */));

app.use(passport.initialize()); // store passport info in req
app.use(passport.session()); // store passport info in session

3. login process

  1. request login from router
  2. passport.authenticate() called from router or controller
  3. follow specified login strategy
  4. if succeed call req.login()
  5. req.login calls passport.serializeUser()
  6. store user id inside req.session
  7. done!

4. create ./passport/index.js

index.js

const passport = require('passport');
const local = require('./localStrategy');
const kakao = require('./kakaoStrategy');
const User = require('../models/user');

module.exports = () => {
  passport.serializeUser((user, done) => {
    done(null, user.id);
  }); // store partial user info to session
  
  passport.deserializeUser((id, done) => {
    User.findOne({ where: { id } })
    	.then(user => done(null, user));
    	.catch(err => done(err));
  }); // restore partial user info from session, query full info from db.
  
  local();
  kakao();
}

5. implement local strategy

isLoggedIn, isNotLoggedIn middlewares

use these middlewares to verify login status

note that passport module adds isAuthenticated() to req object.

module.exports = isLoggedIn = (req, res, next) => {
  if (req.isAuthenticated()) {
    next();
  } else {
    res.status(403).send('login required');
  }
}

module.exports = isNotLoggedIn = (req, res, next) => {
  if (!req.isAuthenticated()) {
    next();
  } else {
    const message = encodeURIComponent('already logged in');
    res.redirect(`/?error=${message}`);
  }
}

auth.js

passport has nothing to do with join process
passport controls login only (at least in below code)

NEVER forget to hashing passwords when creating new user

const hash = await bcrypt.hash(password, 12);
await User.create({email, nick, password:hash,})

login router defines and calls passport.authenticate()
passport.authenticate() calls localStrategy registered in index.js
returned value of passport.authenticate() is used inside of callback(authError, user, info)

req.logout() is now async

router.post('/login', isNotLoggedIn, (req, res, next) => {
  passport.authenticate('local' /* Local Strategy Called HERE */, (authError, user, info) => {
    if (authError) {
      console.error(authError);
      return next(authError);
    }
    if (!user) {
      return res.redirect(`/?loginError=${info.message}`);
    }
    return req.login(user, (loginError) => {
      if (loginError) {
        console.error(loginError);
        return next(loginError);
      }
      return res.redirect('/');
    });
  })(req, res, next);
});

router.get('/logout', isLoggedIn, (req, res, next) => {
  req.logout(() => {
    req.session.destroy(); // remove req.session object
    res.redirect('/');  // remove req.user object
  }); 
});

module.exports = router;

6. implement kakao strategy

책에 있는 내용을 충실히 따라서 구현했다.

7. implement naver strategy

카카오 로그인 기능 학습내용을 응용해보기 위해 naver를 혼자서 구현해봤다.
마침 카카오 서버가 터졌네...

  1. install passport-naver-v2
    npm install passport-naver-v2
  2. register app from developers.naver.com
  3. implement naverStrategy.js
const passport = require('passport');
const NaverStrategy = require('passport-naver-v2').Strategy;

const User = require('../models/user');

module.exports = () => {
  passport.use(new NaverStrategy({
    clientID: process.env.NAVER_ID,
    clientSecret: process.env.NAVER_PW,
    callbackURL: '/auth/naver/callback'
  }, async (accessToken, refreshToken, profile, done) => {
    console.log('naver profile', profile);
    try {
      const exUser = await User.findOne({ where: { snsId: profile.email.split('@')[0], provider: 'naver' } });
      if (exUser) {
        done(null, exUser);
      } else {
        const newUser = await User.create({
          email: profile.email,
          nick: profile.nickname,
          snsId: profile.email.split('@')[0],
          provider: 'naver',
        });
        done(null, newUser);
      }
    } catch (err) {
      console.error(err);
      done(err);
    }
  }));
};
  1. specify routing at auth.js
router.get('/naver', passport.authenticate('naver',{authType:'reprompt'}));

router.get('/naver/callback', passport.authenticate('naver', {
  failureRedirect: '/',
}), (req, res) => {
  res.redirect('/');
});

file upload feature with multer

frontEnd html and server router must be iteractable by proper variable names

need to focus on how to make new formdata and extract file object from html element

frontend html


<!--- input --->

<div>
  <label id="img-label" for="img">사진 업로드</label>
  <input id="img" type="file" accept="image/*" />
  <button id="twit-btn" type="submit" class="btn">짹짹</button>
</div>

<!--- script --->

<script>
  if (document.getElementById('img')) {
    document.getElementById('img').addEventListener('change', function (e) {
  	  // ATTENTION HERE!!
      const formData = new FormData()
      console.log(this, this.files)
      formData.append('img', this.files[0]) // adding file stream to formData
      axios
        .post('/post/img', formData)
        .then((res) => {
          document.getElementById('img-url').value = res.data.url
          document.getElementById('img-preview').src = res.data.url
          document.getElementById('img-preview').style.display = 'inline'
 	  // ATTENTION HERE!!
        })
        .catch((err) => {
          console.error(err)
        })
    })
  }
</script>

express router

const upload = multer({
  storage: multer.diskStorage({
    destination(req, file, cb) {
      cb(null, 'uploads/');
    },
    filename(req, file, cb) {
      const ext = path.extname(file.originalname);
      cb(null, path.basename(file.originalname, ext) + Date.now() + ext);
    }
  }),
  limits: { fileSize: 5 * 1024 * 1024 }
});

// get multer filestream input from formdata[img]
router.post('img', isLoggedIn, upload.single('img'), (req, res, next) => {
  console.log(req.files);
  res.json({ url: `/img/${req.file.filename}` });
  // returns created img uri with res obj;
});

create post router

profile
I'm going from failure to failure without losing enthusiasm

0개의 댓글