Commit 4517a35f authored by Jiwon Yoon's avatar Jiwon Yoon
Browse files

Merge branch 'gyumin'

parents 992699ae 1563c9a4
/node_modules
.env.development
.env
\ No newline at end of file
.env
./upload
\ No newline at end of file
import axios from "axios";
import { baseUrl } from "../utils/baseUrl.js";
import config from "../utils/clientConfig.js";
const getUser = async () => {
const url = `${baseUrl}/api/auth/user`
const { data } = await axios.get(url)
return data
}
const login = async (login) => {
......@@ -32,14 +30,25 @@ const compareId = async (userId) => {
return data
}
const confirmMbnum = async (id, token) => {
const url = `${baseUrl}/api/auth/${id}/${token}`
const confirmMbnum = async (phone) => {
const url = `${baseUrl}/api/auth/phone/${phone}`
const { data } = await axios.post(url)
return data
}
const confirmNum = async (num) => {
const url = `${baseUrl}/api/auth/num/${num}`
const { data } = await axios.get(url)
return data
}
const getNickName = async (id) => {
const url = `${baseUrl}/api/auth/nickname/${id}`
const profile = async (formData) => {
const url = `${baseUrl}/api/auth/profile`
const { data } = await axios.post(url, formData)
return data
}
const getMember = async (id) => {
const url = `${baseUrl}/api/auth/member`
const { data } = await axios.get(url)
return data
}
......@@ -62,7 +71,9 @@ const authApi = {
signup,
compareId,
confirmMbnum,
getNickName,
confirmNum,
profile,
getMember,
comparePw,
modifyUser,
};
......
This diff is collapsed.
......@@ -9,7 +9,17 @@ img{
width: 40%;
}
.confirm{
color: black;
padding-right: 8px;
text-align: center;
display: flex;
align-items: center;
}
.input2 {
width: 9.01rem;
}
.userName{
color: white;
font-size: 1.3rem;
......@@ -28,13 +38,45 @@ width: 40%;
padding-right: 8px;
text-align: left;
}
.input_file_button{
padding: 6px 25px;
background-color:#FF6600;
border-radius: 4px;
color: white;
cursor: pointer;
}
.inputContent{
display: flex;
justify-content: space-around;
align-items: center;
margin: 1rem 0;
}
.box{
width:12rem;
margin: 0px auto;
position: relative;
}
.box:hover{
display: block;
}
.hoverTxt{
display: none;
position:absolute;
top:5rem;
left:2.5rem;
color : #FEDC00;
font-size: 1.5rem;
}
.box:hover .hoverTxt{
display: block;
}
.profile{
width: 12rem;
height: 12rem;
}
.profile:hover{
opacity: 0.5;
}
.input {
margin: 0.5rem 0 0 0;
padding: 0.5rem 0 0.5rem 0;
......
import styles from "./reservation-details.module.scss";
const ReservationDetails = () => {
return(
<div className={`${styles.width}`}>
<header className={`${styles.header}`}>나의 예매 내역</header>
<main>
</main>
</div>
)
}
export default ReservationDetails
\ No newline at end of file
export { default } from "./ReservationDetails"
\ No newline at end of file
.width{
width : 100%;
display: flex;
margin-top: 5rem;
justify-content: center;
}
.header{
display: flex;
justify-content: center;
background-color: #FEDC00;
width: 80%;
text-align: center;
font-size: 2.5rem;
}
\ No newline at end of file
......@@ -14,10 +14,12 @@ const Signup = () => {
userPassword: "",
userRePassword: ""
})
const [number, setNumber] = useState(null);
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
//각 타입별 error 유무 state
const [mbError,setMbError] = useState(false);
const [error, setError] = useState("");
const [errorMsg, setErrorMsg] = useState({
errorId: null,
......@@ -30,6 +32,7 @@ const Signup = () => {
})
// id중복확인 여부 state와 가입하기 누르면 id 임시 저장
const [overlapId, setOverlapId] = useState(false);
const [confirmMb, setConfirmMb] = useState(false);
const [preId, setPreId] = useState("");
//입력할때마다 state에 저장
......@@ -82,12 +85,47 @@ const Signup = () => {
}
const handleOnClickMbnum = async (e) => {
e.preventDefault();
try {
const id = "AC01ecdbffb36dc0766cfea487a54a4c6e";
const token = "1d86d5d43760b5dce5582badf7b0a775";
await authApi.confirmMbnum(id,token);
setError("");
setLoading(true)
const phone = user.userMbnum;
console.log("phone : ", phone)
const message = await authApi.confirmMbnum(phone);
console.log("message : ", message);
if(message.isSuccess){
console.log("mberror: "+mbError);
setMbError("보냄");
}
} catch (error) {
console.log('twilio error'+ error)
console.log('error'+ error)
}finally {
setLoading(false);
}
}
const handleOnChangeMb = (e) => {
setNumber(String(e.target.value));
}
const handleOnClickMbConfirm = async (e) => {
e.preventDefault();
try {
setError("");
setLoading(true)
const confirmNum = number;
console.log(confirmNum)
const message = await authApi.confirmNum(confirmNum);
console.log(message);
setMbError(message);
if(message === "성공"){
setConfirmMb(true);
console.log("인증완료");
}
} catch (error) {
catchErrors(error, setError);
}finally {
setLoading(false);
}
}
......@@ -148,17 +186,26 @@ const Signup = () => {
vaildationIdPw(user.userPassword.length, 8, "errorPassword");
// 비밀번호 확인 유효성 검사
vaildationData(user.userRePassword, user.userPassword, "errorRePassword");
let validation = false;
validation = (Object.values(errorMsg).some((element) => (element)) === false);
// 최종 유효성 검사
if (overlapId && (Object.values(errorMsg).some((element) => (element)) === false)) {
} else if (!overlapId && (Object.values(errorMsg).some((element) => (element)) === false)) {
if (overlapId) {
if(confirmMb){
if(!validation){
throw new Error("유효하지 않은 데이터입니다.");
}else{
console.log("가입성공");
return true
}
}else{
throw new Error("휴대폰 인증도 해주세요");
}
}else{
setErrorMsg(errorMsg => ({ ...errorMsg, errorId: false }));
throw new Error("먼저 아이디 중복확인을 해주세요");
} else {
throw new Error("유효하지 않은 데이터입니다.");
}
}
if (success) {
return <Redirect to="/login" />;
}
......@@ -208,12 +255,29 @@ const Signup = () => {
<div className={styles.inputContent}>
<label className={styles.signupLabel}>휴대폰 번호</label>
<div className="d-flex col-md-auto">
<input className={`${styles.input}`} type="number" name="userMbnum" placeholder="-없이 11자리 입력" onChange={handleUserOnChange} min="" max="99999999999" required />
<button type="button" disabled={loading} name="errorId" className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} onClick={handleOnClickMbnum}>인증번호받기</button>
<input className={`${styles.input} ${styles.input2}`} type="number" name="userMbnum" placeholder="-없이 11자리 입력" onChange={handleUserOnChange} min="" max="99999999999" required />
<button type="button" disabled={loading} className={`rounded-2 mt-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} data-bs-toggle="collapse" data-bs-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample" onClick={handleOnClickMbnum}>인증번호받기</button>
</div>
</div>
{errorMsg.errorMbnum && <p className={styles.passwordConfirmError}>-없이 숫자 11자리를 입력해주세요.</p>}
<div class="collapse" id="collapseExample">
{/* <div className="d-flex col-md-auto justify-content-end"> */}
<div className="d-flex justify-content-between mt-3">
<label className={`${styles.confirm}`}>인증하기</label>
<div>
<input className={`${styles.input} ${styles.input2}`} type="number" placeholder="인증번호를 입력" onChange={handleOnChangeMb} require />
<button type="button" className={`rounded-2 py-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} onClick={handleOnClickMbConfirm}>확인</button>
<button type="button" className={`rounded-2 py-2 ${styles.butterYellowAndBtn} ${styles.btnHover}`} onClick={handleOnClickMbnum}>재전송</button>
</div>
</div>
{(mbError === "재전송") && <p className={styles.passwordConfirmError}>유효시간이 만료되었습니다. 재전송해주세요.</p>}
{(mbError === "보냄") && <p className={styles.passwordConfirmError}>5분이내에 입력해주세요.</p>}
{(mbError === "성공") && <p className={styles.passwordConfirmError}>인증되었습니다.</p>}
{(mbError === "실패") && <p className={styles.passwordConfirmError}>인증번호를 다시 입력해주세요.</p>}
</div>
</div>
<div className="d-flex flex-column">
<div className={`${styles.inputContent}`}>
<label className={styles.signupLabel}>비밀번호</label>
......
......@@ -5,7 +5,13 @@
font-size: 1.7rem;
margin-top: 2rem;
}
.confirm{
color: white;
padding-right: 8px;
text-align: center;
display: flex;
align-items: center;
}
.contents{
display: flex;
width: 100%;
......@@ -31,7 +37,9 @@
border-radius: 3px;
text-align: center;
}
.input2 {
width: 9.01rem;
}
input[type=password]{
font-family: 'Courier New', Courier, monospace;
}
......
import MyInfo from "../components/MyInfo/MyInfo"
import ReservationDetails from "../components/ReservationDetails";
const MyPage = () => {
return (
<div className="d-flex justify-content-center py-4">
<div className="d-flex flex-column justify-content-center align-items-center py-4">
<MyInfo/>
<ReservationDetails/>
</div>
)
}
......
......@@ -421,6 +421,11 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"crypto-js": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
},
"crypto-random-string": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
......@@ -624,6 +629,11 @@
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ="
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
......
......@@ -7,7 +7,7 @@ const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(cookieParser())
app.use('/upload', express.static('upload'))
app.use('/api', mainRouter)
......
import jwt from "jsonwebtoken";
import config from "../config/app.config.js";
import { User, Role, Guest } from '../db/index.js';
import fs from "fs";
import CryptoJS from "crypto-js";
import axios from "axios";
const getUser = async (req, res) => {
try {
......@@ -90,57 +94,152 @@ const compareId = async (req, res) => {
}
}
const confirmMbnum = async (req, res) => {
// const id = req.params.id;
// const token = req.params.token;
// const client = Twilio(id, token);
// // console.log(client);
// client.messages
// .create({
// to: '+8201086074580',
// from: '+14159428621',
// body: '[config.cookieName] 인증번호[1234]를 입력해주세요',
// })
// .then(message => console.log(message.sid))
// .catch(e => console.log(error));
// console.log("id = ", id, "token = ", token);
res.json(true);
// 휴대폰 인증
const NCP_serviceID = 'ncp:sms:kr:270376424445:butterstudio';
const NCP_accessKey = 'GQmVCT2ZFxnEaJOWbrQs';
const NCP_secretKey = 'XLQQ8sd9WxW40hNi0xNBTOG0T8ksRsr8c8sUIEvy';
const date = Date.now().toString();
const uri = NCP_serviceID;
const secretKey = NCP_secretKey;
const accessKey = NCP_accessKey;
const method = 'POST';
const space = " ";
const newLine = "\n";
const url = `https://sens.apigw.ntruss.com/sms/v2/services/${uri}/messages`;
const url2 = `/sms/v2/services/${uri}/messages`;
//시크릿 키를 암호화하는 작업
const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, secretKey);
hmac.update(method);
hmac.update(space);
hmac.update(url2);
hmac.update(newLine);
hmac.update(date);
hmac.update(newLine);
hmac.update(accessKey);
const hash = hmac.finalize();
const signature = hash.toString(CryptoJS.enc.Base64);
let inherentNum = "";
// 유효시간 5분 설정
const time = () => {
inherentNum = false;
return inherentNum
}
// 인증번호 발송
const confirmMbnum = async (req, res) => {
try {
const phoneNumber = req.params.phone;
console.log(phoneNumber);
//인증번호 생성
const verifyCode = Math.floor(Math.random() * (999999 - 100000)) + 100000;
console.log("verifyCode : ",verifyCode);
let result = await axios({
method: method,
json: true,
url: url,
headers: {
'Content-Type': "application/json",
'x-ncp-apigw-timestamp': date,
'x-ncp-iam-access-key': accessKey,
'x-ncp-apigw-signature-v2': signature,
},
data: {
type: 'SMS',
contentType: 'COMM',
countryCode: '82',
from: '01086074580',
content: `[본인 확인] 인증번호 [${verifyCode}]를 입력해주세요.`,
messages: [
{
to: `${phoneNumber}`,
},
],
},
});
const resultMs = result.data.messages;
console.log('resultMs', resultMs);
console.log('response', res.data, res['data']);
inherentNum = String(verifyCode);
// 5분 유효시간 설정
setTimeout(time, 300000);
res.json({ isSuccess: true, code: 202, message: "본인인증 문자 발송 성공", result: res.data });
} catch (error) {
console.log("error: ", error);
if (error.res == undefined) {
res.json({ isSuccess: true, code: 200, message: "본인인증 문자 발송 성공", result: res.data });
}
else res.json({ isSuccess: true, code: 204, message: "본인인증 문자 발송에 문제가 있습니다.", result: error.res });
}
};
// 인증번호 확인
const confirmNum = async (req, res) => {
try {
const verifyCode = inherentNum;
const confirmNum = req.params.num;
if(!verifyCode){
res.send("재전송")
}else{
if (confirmNum !== verifyCode) {
res.send("실패");
}else {
res.send("성공");
}
}
} catch (error) {
console.error("error : ", error.message);
res.status(500).send("잘못된 접근입니다.");
}
};
const signup = async (req, res) => {
const { userId, userEmail, userNickName, userBirthday, userPassword } = req.body;
const { userId, userEmail, userNickName, userBirthday, userMbnum, userPassword } = req.body;
// 휴대폰 중복 확인
const userMbnum = String(req.body.userMbnum);
try {
const mbnum = await User.findOne({ where: { phoneNumber: userMbnum } });
if (mbnum) {
const email = await User.findOne({ where: { email: userEmail } });
if (mbnum && email) {
return res.status(422).send(`이미 있는 이메일, 휴대폰번호입니다.`);
} else if (!mbnum && email) {
return res.status(422).send(`이미 있는 이메일입니다.`);
} else if (mbnum && !email) {
return res.status(422).send(`이미 있는 휴대폰번호입니다.`);
} else {
const role = await Role.findOne({ where: { name: "member" } })
const newUser = await User.create({
userId: userId,
email: userEmail,
nickname: userNickName,
birth: userBirthday,
phoneNumber: userMbnum,
password: userPassword,
img:"",
roleId: role.id
});
res.json(newUser);
}
const role = await Role.findOne({ where: { name: "member" } })
const newUser = await User.create({
userId: userId,
email: userEmail,
nickname: userNickName,
birth: userBirthday,
phoneNumber: userMbnum,
password: userPassword,
roleId: role.id
});
res.json(newUser);
} catch (error) {
console.error(error.message);
res.status(500).send("회원가입 에러. 나중에 다시 시도 해주세요");
}
};
const getNickName = async (req, res) => {
const getMember = async (req, res) => {
try {
const token = req.cookies.butterStudio;
const decoded = jwt.verify(token, config.jwtSecret);
if (decoded.role === "member") {
const user = await User.findOne({ where: { id: decoded.id }, attributes: ["nickname"] });
res.json(user.nickname);
const user = await User.findOne({ where: { id: decoded.id } });
res.json({ nickname: user.nickname, img: user.img });
} else {
res.status(401).send("잘못된 접근입니다.");
}
......@@ -150,6 +249,34 @@ const getNickName = async (req, res) => {
}
}
const uploadProfile = async (req, res) => {
try {
const image = req.file.filename;
console.log(req.file);
const token = req.cookies.butterStudio;
const decoded = jwt.verify(token, config.jwtSecret);
if (decoded) {
const img = await User.findOne({ where: { id: decoded.id }, attributes: ["img"] });
console.log("여기여기");
fs.unlink("upload" + `\\${img.img}`, function (data) { console.log(data); });
const user = await User.update({
img: image
}, { where: { id: decoded.id } });
if (user) {
const success = await User.findOne({ where: { id: decoded.id }, attributes: ["img"] });
res.json(success)
} else {
throw new Error("프로필 등록 실패")
}
}
} catch (error) {
console.error(error.message);
res.status(500).send("프로필 에러");
}
}
const comparePw = async (req, res) => {
try {
//쿠키 안 토큰에서 id추출
......@@ -159,6 +286,7 @@ const comparePw = async (req, res) => {
const user = await User.scope("withPassword").findOne({ where: { id: decoded.id } });
//입력한 비번과 해당 행 비번을 비교
const passwordMatch = await user.comparePassword(req.params.pw);
console.log("passwordMatch : ", passwordMatch);
//클라이언트로 동일여부를 전송
if (passwordMatch) {
return res.json(true)
......@@ -171,26 +299,54 @@ const comparePw = async (req, res) => {
}
}
const overlap = async (decoded, dataType, data) => {
try {
let overlap = await User.findOne({ where: { id: decoded.id } });
console.log("기존 데이터 : ", overlap, "변경할 데이터 : ", data);
if (overlap[dataType] === data) {
console.log("여기여기")
return true
} else {
let overlap2 = await User.findOne({ attributes: [dataType] });
console.log(overlap2)
if (overlap2[dataType] === data) {
return false
} else {
return true
}
}
} catch (error) {
console.error(error.message);
}
}
const modifyUser = async (req, res) => {
try {
const token = req.cookies.butterStudio;
const decoded = jwt.verify(token, config.jwtSecret);
const { userEmail, userNickName, userMbnum, userPassword } = req.body;
const emailOverlap = await User.findOne({ where: { email: userEmail } });
console.log(userEmail);
console.log(userMbnum);
if (emailOverlap) {
return res.status(422).send(`이미 있는 이메일입니다.`);
const overlapEmail = await overlap(decoded, "email", userEmail);
const overlapMbnum = await overlap(decoded, "phoneNumber", userMbnum);
console.log("overlapEmail", overlapEmail, " overlapMbnum : ", overlapMbnum);
if (overlapEmail && overlapMbnum) {
const user = await User.update({
email: userEmail,
nickname: userNickName,
phoneNumber: userMbnum,
password: userPassword,
}, { where: { id: decoded.id } });
console.log("user22 :", user);
res.json(user);
} else if (!overlapEmail && overlapMbnum) {
res.status(500).send("이미 있는 이메일입니다.");
} else if (overlapEmail && !overlapMbnum) {
res.status(500).send("이미 있는 핸드폰번호입니다.");
} else {
if (decoded) {
let user = await User.findOne({ where: { id: decoded.id } });
await user.update({
email: userEmail,
nickname: userNickName,
phoneNumber: userMbnum,
password: userPassword,
});
res.json(user);
}
res.status(500).send("이미 있는 이메일, 핸드폰번호입니다.");
}
} catch (error) {
console.error(error.message);
......@@ -236,10 +392,12 @@ export default {
logout,
compareId,
confirmMbnum,
confirmNum,
signup,
getNickName,
comparePw,
modifyUser,
getUserInfo,
saveGuestInfo
saveGuestInfo,
getMember,
uploadProfile,
getUserInfo
}
......@@ -28,10 +28,11 @@ sequelize
birth: "990926",
phoneNumber: "01086074580",
password: "admin!",
img: "970aaa79673a39331d45d4b55ca05d25",
roleId: adminRole?.id,
});
} else {}
} else { }
app.listen(appConfig.port, () => {
console.log(`Server is running on port ${appConfig.port}`);
});
......
......@@ -14,23 +14,26 @@ const UserModel = (sequelize) => {
autoIncrement: true,
},
userId: {
type: DataTypes.STRING,
type: DataTypes.STRING
},
email: {
type: DataTypes.STRING,
type: DataTypes.STRING
},
nickname: {
type: DataTypes.STRING,
type: DataTypes.STRING
},
birth: {
type: DataTypes.STRING,
type: DataTypes.STRING
},
phoneNumber: {
type: DataTypes.STRING
},
password: {
type: DataTypes.STRING,
type: DataTypes.STRING
},
img: {
type: DataTypes.STRING
}
},
{
timestamps: true,
......
......@@ -6,7 +6,6 @@ import timetableRouter from "./timetable.route.js";
import cinemaRouter from "./cinema.route.js";
import kakaopayRouter from "./kakaopay.route.js";
import emailRouter from './email.route.js'
import theaterRouter from './theater.route.js'
import reservationRouter from './reservation.route.js'
const router = express.Router();
......
import express from "express";
import userCtrl from "../controllers/user.controller.js";
import multer from "multer"
const router = express.Router();
const upload = multer({
dest: "upload/"
})
router
.route("/user")
......@@ -19,14 +23,16 @@ router
.route("/signup")
.post(userCtrl.signup)
router
.post("/profile", upload.single("image"), userCtrl.uploadProfile)
router
.route("/modify")
.post(userCtrl.modifyUser)
router
.route("/nickname/:id")
.get(userCtrl.getNickName)
.route("/member")
.get(userCtrl.getMember)
router
.route("/pw/:pw")
......@@ -37,8 +43,12 @@ router
.get(userCtrl.compareId)
router
.route("/:id/:token")
.get(userCtrl.confirmMbnum)
.route("/phone/:phone")
.post(userCtrl.confirmMbnum)
router
.route("/num/:num")
.get(userCtrl.confirmNum)
router.route('/getuserinfo')
.post(userCtrl.getUserInfo)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment