Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions BackEnd/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions BackEnd/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
"express-validator": "^7.0.1",
"helmet": "^7.0.0",
"jsonwebtoken": "^9.0.1",
"memory-cache": "^0.2.0",
"method-override": "^3.0.0",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"multer-s3": "^2.10.0",
"mysql2": "^2.3.3",
"nodemailer": "^6.9.4",
"sequelize": "^6.32.1",
"sequelize-cli": "^6.4.1",
"winston": "^3.8.2",
Expand Down
139 changes: 131 additions & 8 deletions BackEnd/src/controllers/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const user = require('../services/user');
const { success, fail } = require('../functions/responseStatus');

const { startDate, endDate, todayDate, firstDay } = require('../functions/common');
const { verifycodeMail } = require('../functions/nodemail');

/**
* 제공된 이메일과 비밀번호로 로그인을 시도하고, 성공하면 토큰을 발급한다.
Expand Down Expand Up @@ -143,7 +144,7 @@ const updateProfile = async (req, res) => {

/**
* 사용자의 id로 오늘 출석 했는지를 조회합니다.
* @param {number} id
* @param {number} id
* @returns {object} { code: number, message: string }
* 출석했다면 409을 반환
* 출석하지 않았다면 출석 체크를 하고 200반환
Expand Down Expand Up @@ -190,15 +191,53 @@ const getAttendance = async (req, res) => {
};

/**
* 현재 비밀번호, 새 비밀번호, 비밀번호 확인 입력받아 비밀번호 변경
* @param {string} confirm_password 사용자가 입력한 기존 비밀번호
* 비밀번호 재확인
* @param {string} confirm_password 기존 비밀번호
*
*/
const checkPassword = async (req, res) => {
let { confirm_password } = req.body;
let user_id = req.decoded.id;
try {
//비밀번호 확인
let result = await user.comparePassword(confirm_password, user_id);
if (result) {
//일회성 토큰 발급
await user.issueOneTimeToken(result).then((data) => {
let token = data;
return success(res, 200, 'Authorize success.', token);
});
}
} catch (err) {
let code;
switch (err.message) {
case 'Incorrect password.':
code = 401;
break;
case 'Can not find profile.':
code = 404;
break;
default:
code = 500;
break;
}
return fail(res, code, err.message);
}
};

/**
* 새 비밀번호 입력받아 비밀번호 변경
* @param {string} new_password 새 비밀번호
*/
const editPassword = async (req, res) => {
let { confirm_password, new_password } = req.body;
let { new_password } = req.body;
let user_id = req.decoded.id;
//console.log(req.headers.authorization);
try {
let result = await user.updatePassword(user_id, confirm_password, new_password);
if (req.decoded.type !== 'OneTimeJWT') {
throw new Error('token is invalid.');
}
let result = await user.updatePassword(user_id, new_password);
if (result.message === 'Password changed.') {
let data = result.user;
return success(res, 200, result.message, data);
Expand All @@ -208,11 +247,76 @@ const editPassword = async (req, res) => {
} catch (err) {
let code;
switch (err.message) {
case 'token is invalid.':
code = 403; // 403일 경우 위치 변경
break;
case 'Can not find profile.':
code = 404;
break;
case 'Incorrect password.':
code = 401;
default:
code = 500;
break;
}
return fail(res, code, err.message);
}
};

/**
* email을 받아 확인후 인증번호 메일전송
* @param {string} email 사용자가 입력한 기존 비밀번호
*/
const sendVerifyEmail = async (req, res) => {
let { email } = req.body;
try {
let result = await user.findUser('email', email, 0);
if (result) {
// 인증번호, 캐시저장
let verifycode = await user.verifycode(email);
console.log(verifycode); // 임시
//인증번호 전송 메일 (비동기)
verifycodeMail(email, verifycode);
return success(res, 200);
} else {
throw new Error('Services error.');
}
} catch (err) {
let code;
switch (err.message) {
case 'Can not find profile.':
code = 404;
break;
default:
code = 500;
break;
}
return fail(res, code, err.message);
}
};

/**
* 이메일, 인증번호 입력받아 확인
* @param {string} email 사용자가 받는데 사용한 이메일
* @param {number} verifycode 입력한 인증번호
*/
const checkVerifyCode = async (req, res) => {
let { email, verifycode } = req.body;
try {
//인증번호 확인
let result = user.checkCode(email, verifycode);
//새비밀번호 페이지로 넘기기
if (result) {
//일회성 토큰 발급
await user.issueOneTimeToken(email).then((data) => {
let token = data;
return success(res, 200, 'Authorize success.', token);
});
}
} catch (err) {
let code;
switch (err.message) {
case "Code dosesn't match.": //401이나 403?
case 'Verifycode expired.':
code = 409;
break;
default:
code = 500;
Expand Down Expand Up @@ -256,11 +360,25 @@ const viewAttend = (req, res) => {
});
};

/**
* 비밀번호 확인페이지를 렌더링한다.
*/
const viewVerifyPassword = (req, res) => {
res.render('user/verifyPassword');
};

/**
* 비밀번호 변경페이지를 렌더링한다.
*/
const viewChangePassword = (req, res) => {
res.render('user/password');
res.render('user/newPassword');
};

/**
* 메일 인증페이지를 렌더링한다.
*/
const viewverifyEmail = (req, res) => {
res.render('user/verifyEmail');
};

module.exports = {
Expand All @@ -270,10 +388,15 @@ module.exports = {
updateProfile,
postAttendance,
getAttendance,
checkPassword,
editPassword,
sendVerifyEmail,
checkVerifyCode,
viewLogin,
viewRegister,
viewProfile,
viewAttend,
viewVerifyPassword,
viewChangePassword,
viewverifyEmail,
};
31 changes: 31 additions & 0 deletions BackEnd/src/functions/nodemail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';

const nodemailer = require('nodemailer');
const config = require('../../config/default.json');
const logger = require('./winston');

const transporter = nodemailer.createTransport({
service: 'gmail',
port: 587, // 보안없는경우 587, 있는경우 465로 설정. 기본은 587
auth: {
user: config.mail_info.user,
pass: config.mail_info.pass,
},
});

exports.verifycodeMail = async (email, code) => {
let mailOptions = {
from: config.mail_info.user,
to: email,
subject: 'Verifying Code by CSW_BOARD',
text: `Your Verifycode is ${code}.`,
};
let send = await transporter.sendMail(mailOptions);
if (send) {
logger.info(`'send verifycode to ${email}'`);
//console.log('send OK');
return 'Mail send success.';
} else {
throw new Error('Mail send fail.');
}
};
8 changes: 8 additions & 0 deletions BackEnd/src/functions/signJWT.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ const refreshToken = (payload) => {
});
};

const oneTimeToken = (payload) => {
return jwt.sign(payload, access_secret_key, {
expiresIn: '5m',
issuer: config.get('JWT.issuer'),
});
};

const issuanceToken = (req, res) => {
return jwt.verify(req.headers.authorization, refresh_secret_key, (err, decoded) => {
if (err) {
Expand All @@ -41,5 +48,6 @@ const issuanceToken = (req, res) => {
module.exports = {
accessToken,
refreshToken,
oneTimeToken,
issuanceToken,
};
36 changes: 33 additions & 3 deletions BackEnd/src/routes/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,38 @@ router.patch(
router.post('/attendance', auth, ctrl.postAttendance);
router.get('/attendance', auth, ctrl.getAttendance);

router.post(
'/verifyPassword',
auth,
[
check('confirm_password', 'Password must be shorter than 101 characters.').isLength({ max: 100 }),
validator,
],
ctrl.checkPassword);

router.post('/sendEmail', [
check('email')
.isEmail()
.withMessage('Email must be in the correct format.')
.isLength({ max: 30 })
.withMessage('Email must be shorter than 31 characters.'),
validator,
], ctrl.sendVerifyEmail);

router.post('/verifyEmail', [
check('email')
.isEmail()
.withMessage('Email must be in the correct format.')
.isLength({ max: 30 })
.withMessage('Email must be shorter than 31 characters.'),
check('verifycode', 'Please input code.').notEmpty(),
validator,
], ctrl.checkVerifyCode);

router.patch(
'/profile/password',
'/newPassword',
auth,
[
check('confirm_password', 'Please input password.').notEmpty(),
check('new_password', 'Password must be longer than 2 characters & shorter than 101 characters.').isLength({ min: 3, max: 100 }),
validator,
],
Expand All @@ -80,6 +107,9 @@ router.get('/login', ctrl.viewLogin);
router.get('/register', ctrl.viewRegister);
router.get('/profile/output/', ctrl.viewProfile);
router.get('/attendance/output', ctrl.viewAttend);
router.get('/profile/password', ctrl.viewChangePassword);
router.get('/verifyPassword', ctrl.viewVerifyPassword); // 비밀번호 확인 페이지
router.get('/verifyEmail', ctrl.viewverifyEmail); // 인증번호 보내고 체크하는 페이지
router.get('/newPassword', ctrl.viewChangePassword); // 비밀번호 변경 페이지


module.exports = router;
Loading