From e1467552d59c8a7b01ed51498613af14a8275056 Mon Sep 17 00:00:00 2001 From: Ji Min Lee Date: Wed, 9 Aug 2023 19:30:08 +0900 Subject: [PATCH] Feat : Github Login & routing (#52) * feat : email verify modal dev start * feat : verify modal ui * feat : register modal api && login api connect * feat : gh login 1. login for registerd user --- package-lock.json | 1 + package.json | 1 + src/App.tsx | 5 +- src/assets/images/ghLogo.png | Bin 0 -> 23217 bytes src/assets/images/profile.png | Bin 0 -> 32174 bytes src/components/Modal/EmailVerifyModal.css | 11 + src/components/Modal/EmailVerifyModal.js | 57 +++++ src/navigation/AppRouter.tsx | 62 +++--- src/navigation/LeftTeamSection.css | 43 ++-- src/navigation/TopNavBar.tsx | 202 +++++++++--------- src/pages/Login/GithubLogin.css | 21 ++ src/pages/Login/GithubLogin.js | 70 ++++++ src/pages/Login/Login.css | 3 +- src/pages/Login/Login.js | 42 +--- src/pages/MyProjects/MyProjects.css | 136 ++++++------ src/pages/Profile/Profile.css | 19 ++ src/pages/Profile/Profile.tsx | 24 +++ src/pages/Register/Registerform.css | 2 +- src/pages/Register/Registerform.js | 38 +++- .../authentication/authentication.context.js | 188 ++++++++++------ .../authentication/authentication.service.tsx | 163 +++++++------- src/service/authentication/github.service.tsx | 53 +++++ src/setupProxy.js | 18 ++ tsconfig.json | 21 ++ 24 files changed, 778 insertions(+), 402 deletions(-) create mode 100644 src/assets/images/ghLogo.png create mode 100644 src/assets/images/profile.png create mode 100644 src/components/Modal/EmailVerifyModal.css create mode 100644 src/components/Modal/EmailVerifyModal.js create mode 100644 src/pages/Login/GithubLogin.css create mode 100644 src/pages/Login/GithubLogin.js create mode 100644 src/pages/Profile/Profile.css create mode 100644 src/pages/Profile/Profile.tsx create mode 100644 src/service/authentication/github.service.tsx create mode 100644 src/setupProxy.js diff --git a/package-lock.json b/package-lock.json index 741e29e..a2dc787 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "axios": "^1.4.0", "bootstrap": "^5.3.0", "frappe-gantt": "^0.6.1", + "http-proxy-middleware": "^2.0.6", "moment": "^2.29.4", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", diff --git a/package.json b/package.json index 5eefd85..c182d2e 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "axios": "^1.4.0", "bootstrap": "^5.3.0", "frappe-gantt": "^0.6.1", + "http-proxy-middleware": "^2.0.6", "moment": "^2.29.4", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", diff --git a/src/App.tsx b/src/App.tsx index ebe616b..dd04b71 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,12 +3,15 @@ import { AuthenticationContextProvider } from "./service/authentication/authenti import AppRouter from "./navigation/AppRouter"; import "bootstrap/dist/css/bootstrap.min.css"; import { TeamsContextProvider } from "./service/teams/teams.context"; +import { ProjectsContextProvider } from "./service/projects/projects.context"; const App: React.FC = () => { return ( - + + + ); diff --git a/src/assets/images/ghLogo.png b/src/assets/images/ghLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..57bc3fe290d5d37f84317cbef7534a02a3e1df83 GIT binary patch literal 23217 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 bMBx3peWrVP$Nld)cHo7QhT?a5i{SqO&aiUd literal 0 HcmV?d00001 diff --git a/src/components/Modal/EmailVerifyModal.css b/src/components/Modal/EmailVerifyModal.css new file mode 100644 index 0000000..4b735a3 --- /dev/null +++ b/src/components/Modal/EmailVerifyModal.css @@ -0,0 +1,11 @@ +.EmailVerifyModal .inputLabel { + margin-bottom: 16px; +} +.EmailVerifyModal .infoBox { + background-color: rgba(218, 218, 218, 0.33); + padding: 4%; + border-radius: 20px; + margin-top: 40px; +} +.EmailVerifyModal .infoText { +} diff --git a/src/components/Modal/EmailVerifyModal.js b/src/components/Modal/EmailVerifyModal.js new file mode 100644 index 0000000..f84dbd2 --- /dev/null +++ b/src/components/Modal/EmailVerifyModal.js @@ -0,0 +1,57 @@ +import React, { useContext, useState } from "react"; +import { Button, Modal, Form } from "react-bootstrap"; +import "./EmailVerifyModal.css"; +import { BsDot } from "react-icons/bs"; + +export const EmailVerifyModal = ({ show, handleClose, checkNumber, setCheckNumber, CheckEmailMessageHandler }) => { + return ( + + + 인증번호 + + +
+ + 이메일로 인증번호 전송이 완료되었습니다! + setCheckNumber(e.target.value)} /> + +
+
+

인증번호 문자를 못 받으셨나요?

+
+ +

+ 입력하신 인증정보가 일치하지 않을 경우, 인증번호 문자 +
는 발송되지 않습니다. +

+
+
+ +

+ 인증번호가 문자로 수신되지 않을 경우 정확한 정보로 재 +
시도해 주시기 바랍니다. +

+
+
+
+ + + + +
+ ); +}; diff --git a/src/navigation/AppRouter.tsx b/src/navigation/AppRouter.tsx index 8d6d44f..eb5c2ae 100644 --- a/src/navigation/AppRouter.tsx +++ b/src/navigation/AppRouter.tsx @@ -15,38 +15,38 @@ import MyIssues from "../pages/MyIssues/MyIssues"; import TeamInfo from "../pages/TeamInfo/TeamInfo"; import TeamSetting from "../pages/TeamSetting/TeamSetting"; import { ProjectAccess } from "../pages/ProjectSetting/ProjectAccess"; -import { ProjectSetting } from "../pages/ProjectSetting/projectSetting"; -import { ProjectsContextProvider } from "../service/projects/projects.context"; -import React from "react"; +import { ProjectSetting } from "../pages/ProjectSetting/ProjectSetting"; +import { Profile } from "../pages/Profile/Profile"; +import { GithubLogin } from "../pages/Login/GithubLogin"; const AppRouter = (): JSX.Element => { - return ( - - - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - }> - } /> - } /> - } /> - - }> - } /> - } /> - } /> - } /> - - - - - - ); + return ( + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + }> + } /> + } /> + } /> + + }> + } /> + } /> + } /> + } /> + + + + + ); }; export default AppRouter; diff --git a/src/navigation/LeftTeamSection.css b/src/navigation/LeftTeamSection.css index f0bce8f..957cd26 100644 --- a/src/navigation/LeftTeamSection.css +++ b/src/navigation/LeftTeamSection.css @@ -1,39 +1,40 @@ .LeftTeamSection { - display: flex; - width: 100%; - background-color: #e9e3f5; + display: flex; + width: 100%; + height: 100%; + background-color: #e9e3f5; } .LeftTeamSection .container { - display: flex; - margin: 4%; - padding: 4%; - flex-direction: column; - align-items: center; - width: 200px; - height: fit-content; - border-radius: 50px; - background-color: white; - box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; + display: flex; + margin: 4%; + padding: 4%; + flex-direction: column; + align-items: center; + width: 200px; + height: fit-content; + border-radius: 50px; + background-color: white; + box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; } .LeftTeamSection .all-button, .LeftTeamSection .private-button { - width: 100px; - margin-top: 10px; + width: 100px; + margin-top: 10px; } .LeftTeamSection .teams { - display: flex; - flex-direction: column; - align-items: flex-start; - margin-top: 20px; + display: flex; + flex-direction: column; + align-items: flex-start; + margin-top: 20px; } .LeftTeamSection .teams-title { } .LeftTeamSection .teams Button { - margin-top: 10px; - width: 100px; + margin-top: 10px; + width: 100px; } diff --git a/src/navigation/TopNavBar.tsx b/src/navigation/TopNavBar.tsx index e384e1f..e20bafe 100644 --- a/src/navigation/TopNavBar.tsx +++ b/src/navigation/TopNavBar.tsx @@ -14,116 +14,122 @@ import Notification from "../components/Notification/Notification"; import MyWork from "../components/MyWork/MyWork"; interface LoggedInNavProps { - onNotificationClick: () => void; - onMyWorkClick: () => void; + onNotificationClick: () => void; + onMyWorkClick: () => void; } const LoggedInNav = ({ onNotificationClick, onMyWorkClick }: LoggedInNavProps) => { - const { OnLogout } = useContext(AuthenticationContext); - const navigate = useNavigate(); + const { OnLogout } = useContext(AuthenticationContext); + const navigate = useNavigate(); - return ( - <> - - - - - 알람 - 설정 - 프로필 - { - OnLogout(); - }} - > - 로그아웃 - - - - ); + return ( + <> + + + + + 알람 + 설정 + { + navigate("/profile"); + }} + > + 프로필 + + { + OnLogout(); + }} + > + 로그아웃 + + + + ); }; const LoggedOutNav = () => { - const navigate = useNavigate(); - return ( - <> - - - - - navigate("/login")}>Sign In - navigate("/registerform")}>Join - - - ); + const navigate = useNavigate(); + return ( + <> + + + + + navigate("/login")}>Sign In + navigate("/registerform")}>Join + + + ); }; export const TopNavBar = () => { - const { isLogin } = useContext(AuthenticationContext); - const [showNotification, setShowNotification] = useState(false); - const [showMyWork, setShowMyWork] = useState(false); - const navigate = useNavigate(); - const handleNotificationClick = () => { - setShowNotification(true); - }; + const { isLogin } = useContext(AuthenticationContext); + const [showNotification, setShowNotification] = useState(false); + const [showMyWork, setShowMyWork] = useState(false); + const navigate = useNavigate(); + const handleNotificationClick = () => { + setShowNotification(true); + }; - const handleCloseNotification = () => { - setShowNotification(false); - }; + const handleCloseNotification = () => { + setShowNotification(false); + }; - const handleMyWorkClick = () => { - setShowMyWork(true); - }; + const handleMyWorkClick = () => { + setShowMyWork(true); + }; - const handleCloseMywork = () => { - setShowMyWork(false); - }; + const handleCloseMywork = () => { + setShowMyWork(false); + }; - return ( -
- - - { - navigate("/"); - }} - style={{ cursor: "pointer" }} - > - - A-Log - - - {isLogin === true ? : } - - - - - -
- ); + return ( +
+ + + { + navigate("/"); + }} + style={{ cursor: "pointer" }} + > + + A-Log + + + {isLogin === true ? : } + + + + + +
+ ); }; diff --git a/src/pages/Login/GithubLogin.css b/src/pages/Login/GithubLogin.css new file mode 100644 index 0000000..a9d7662 --- /dev/null +++ b/src/pages/Login/GithubLogin.css @@ -0,0 +1,21 @@ +.GithubLogin { + margin-top: 10%; + width: 100%; + height: 100%; + display: flex; + justify-content: center; +} +.GithubLogin .container { + width: fit-content; + justify-content: center; +} +.GithubLogin .ghLogo { + width: 200px; +} +.GithubLogin .loadingWrapper { + margin-top: 40px; + display: flex; + align-items: center; + width: 170px; + justify-content: space-between; +} diff --git a/src/pages/Login/GithubLogin.js b/src/pages/Login/GithubLogin.js new file mode 100644 index 0000000..d3ff385 --- /dev/null +++ b/src/pages/Login/GithubLogin.js @@ -0,0 +1,70 @@ +import { useContext, useEffect } from "react"; +import { GithubAuth } from "../../service/authentication/github.service"; +import { useLocation, useNavigate } from "react-router-dom"; +import { AuthenticationContext } from "../../service/authentication/authentication.context"; +import { FloatingWrapper } from "../../components/FloatingWrapper"; +import { Spinner } from "react-bootstrap"; +import GithubLogo from "../../assets/images/ghLogo.png"; +import "./GithubLogin.css"; +export const GithubLogin = () => { + const location = useLocation(); + const navigate = useNavigate(); + const { OnGHLogin, isLogin } = useContext(AuthenticationContext); + + useEffect(() => { + if (isLogin === true) { + navigate("/"); + } + }, [isLogin]); + useEffect(() => { + if (isLogin === true) { + navigate("/"); + } + }, []); + useEffect(() => { + if (location.search) { + fetchGithubAuth(); + } + }, []); + + const fetchGithubAuth = async () => { + const code = urlArgs().code; + const accessToken = await GithubAuth(code); // await를 사용 + const loginres = await OnGHLogin(accessToken); + console.log(loginres); + if (loginres.type === "email") { + navigate("/registerform", { state: { email: loginres.result } }); + } else { + } + }; + + const urlArgs = () => { + var args = {}; + var query = location.search.substring(1); + var pairs = query.split("&"); + for (var i = 0; i < pairs.length; i++) { + var pos = pairs[i].indexOf("="); + if (pos === -1) { + continue; + } + var name = pairs[i].substring(0, pos); + var value = pairs[i].substring(pos + 1); + value = decodeURIComponent(value); + args[name] = value; + } + console.log(args); + return args; + }; + + return ( +
+ + +
+
깃허브 로그인중...
+ +
+
+
+ ); +}; diff --git a/src/pages/Login/Login.css b/src/pages/Login/Login.css index c6d9d18..0cad111 100644 --- a/src/pages/Login/Login.css +++ b/src/pages/Login/Login.css @@ -3,7 +3,8 @@ flex-direction: column; justify-content: center; align-items: center; - margin-top: 7%; + margin-top: 4%; + margin-bottom: 4%; } .Login .center { diff --git a/src/pages/Login/Login.js b/src/pages/Login/Login.js index 2ab1d30..5290fb5 100644 --- a/src/pages/Login/Login.js +++ b/src/pages/Login/Login.js @@ -2,12 +2,13 @@ import React, { useContext, useEffect, useState } from "react"; import Button from "react-bootstrap/Button"; import "./Login.css"; import logo from "../../assets/logo/alog-logo.png"; -import { GitHubLoginRequestHandler } from "../../service/authentication/authentication.service"; + import { useLocation, useNavigate } from "react-router-dom"; import { FloatingWrapper } from "../../components/FloatingWrapper"; import FadeIn from "../../animation/FadeIn"; import { TextButton } from "../../components/Buttons"; import { AuthenticationContext } from "../../service/authentication/authentication.context"; +import { GitHubLoginRequestHandler, GithubAuth } from "../../service/authentication/github.service"; const Login = () => { const location = useLocation(); @@ -18,7 +19,7 @@ const Login = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); - const LoginHandler = (email, password) => { + const LoginHandler = () => { OnLogin(email, password); }; @@ -32,37 +33,10 @@ const Login = () => { navigate("/"); } }, []); - // 깃허브로 로그인후 동작하는 코드. - useEffect(() => { - if (location.search) { - function urlArgs() { - var args = {}; - var query = location.search.substring(1); - var pairs = query.split("&"); - for (var i = 0; i < pairs.length; i++) { - // '이름=값' 패턴을 찾는다. - var pos = pairs[i].indexOf("="); - // 찾을 수 없다면 스킵한다. - if (pos === -1) { - continue; - } - // 이름을 추출한다. - var name = pairs[i].substring(0, pos); - // 값을 추출한다. - var value = pairs[i].substring(pos + 1); - // 값을 해석한다. - value = decodeURIComponent(value); - // 프로퍼티로 저장한다. - args[name] = value; - } - // 추출된 전달인자들을 반환한다. - console.log(args); - navigate(`/registerform`, { state: { email: "asdf@gmail.com" } }); - return args; - } - urlArgs(); - } - }, []); + + const OnGithubLogin = () => { + GitHubLoginRequestHandler(); + }; return ( @@ -86,7 +60,7 @@ const Login = () => { - + */}
Password
diff --git a/src/service/authentication/authentication.context.js b/src/service/authentication/authentication.context.js index 1e6d1e2..69b474b 100644 --- a/src/service/authentication/authentication.context.js +++ b/src/service/authentication/authentication.context.js @@ -1,82 +1,132 @@ import React, { useState, createContext, useEffect } from "react"; -import { PostSendVerifyEmail, PostVerifyEmail, UsersCheckDuplicate, UsersLogin, UsersSignup, loginHandler } from "./authentication.service"; +import { + GetEmailWithGHToken, + PermitAllEmailLogin, + PostSendVerifyEmail, + PostVerifyEmail, + UsersCheckDuplicate, + UsersLogin, + UsersSignup, + loginHandler, + permitAllEmailLogin, +} from "./authentication.service"; export const AuthenticationContext = createContext(); export const AuthenticationContextProvider = ({ children }) => { - const [userToken, setUserToken] = useState(JSON.parse(sessionStorage.getItem("userToken"))); - const [isLogin, setIsLogin] = useState(JSON.parse(sessionStorage.getItem("isLogin"))); - const [userData, setUserData] = useState(null); + const [userToken, setUserToken] = useState(JSON.parse(sessionStorage.getItem("userToken"))); + const [isLogin, setIsLogin] = useState(JSON.parse(sessionStorage.getItem("isLogin"))); + const [userData, setUserData] = useState(JSON.parse(sessionStorage.getItem("userData"))); - const OnLogin = async (userEmail, userPw) => { - const res = await UsersLogin(userEmail, userPw); - console.log(res); - sessionStorage.setItem("isLogin", true); - setIsLogin(true); - alert("로그인 되었습니다!"); - }; + const OnLogin = async (userEmail, userPw) => { + const res = await UsersLogin(userEmail, userPw); + console.log(res); + if (res.data === "login failed") { + alert("이메일과 비밀번호를 다시 확인해주세요."); + } else { + setUserToken(res.data); + setUserData(JSON.parse(atob(res.data.split(".")[1]))); + sessionStorage.setItem("userData", JSON.stringify(atob(res.data.split(".")[1]))); + sessionStorage.setItem("userToken", JSON.stringify(res.data)); + sessionStorage.setItem("isLogin", true); + setIsLogin(true); + alert("로그인 되었습니다!"); + } + }; - const OnLogout = () => { - setUserToken(""); - window.location.reload(); - setIsLogin(false); - sessionStorage.removeItem("isLogin"); - alert("로그아웃 되었습니다!"); - }; + const OnLogout = () => { + setUserToken(""); + window.location.reload(); + setIsLogin(false); + sessionStorage.removeItem("isLogin"); + sessionStorage.removeItem("userData"); + sessionStorage.removeItem("userToken"); + alert("로그아웃 되었습니다!"); + }; - const OnRegister = async (email, userPW, userNN) => { - const res = await UsersSignup(email, userPW, userNN) - .then(alert(`회원가입이 완료되었습니다`)) - .catch((e) => alert(e)); - console.log(res); - }; + const OnRegister = async (email, userPW, userNN) => { + const res = await UsersSignup(email, userPW, userNN) + .then(alert(`회원가입이 완료되었습니다`)) + .catch((e) => alert(e)); + console.log(res); + }; - const OnDupNNCheck = async (userNN) => { - const res = await UsersCheckDuplicate(userNN) - .then((res) => { - console.log(res.data); - return res.data; - }) - .catch((e) => alert(e)); + const OnDupNNCheck = async (userNN) => { + const res = await UsersCheckDuplicate(userNN) + .then((res) => { + console.log(res.data); + return res.data; + }) + .catch((e) => alert(e)); - return res; - }; + return res; + }; - const OnEmailVerifySend = async (email) => { - const res = await PostSendVerifyEmail(email) - .then((res) => { - console.log(res.data); - alert(res.data); + const OnEmailVerifySend = async (email) => { + const res = await PostSendVerifyEmail(email) + .then((res) => { + console.log(res.data); + alert(res.data); + return res; + }) + .catch((e) => alert(e)); return res; - }) - .catch((e) => alert(e)); - return res; - }; - const OnEmailVerify = async (email, code) => { - const res = await PostVerifyEmail(email, code) - .then((res) => { - console.log(res); - alert(res.data); - if (res.data == "Email is Verified Successfully") { - return true; - } else return false; - }) - .catch((e) => alert(e)); - return res; - }; - return ( - - {children} - - ); + }; + const OnEmailVerify = async (email, code) => { + const res = await PostVerifyEmail(email, code) + .then((res) => { + console.log(res); + alert(res.data); + if (res.data == "Email is Verified Successfully") { + return true; + } else return false; + }) + .catch((e) => alert(e)); + return res; + }; + + const OnGHLogin = async (accessToken) => { + try { + const ghEmail = await GetEmailWithGHToken(accessToken); + console.log(ghEmail); + if (ghEmail === null) { + alert("이메일을 알 수 없는 깃허브 계정입니다"); + } + const emailCheck = await PermitAllEmailLogin(ghEmail); + console.log(emailCheck); + const res = emailCheck.data.split(" "); + if (res[0] === "email") { + alert("회원가입창으로 이동합니다 닉네임을 설정해주세요!"); + console.log("email"); + return { type: "email", result: res[1] }; + } else { + console.log("jwt : ", res[1]); + setUserToken(res[1]); + setUserData(JSON.parse(atob(res[1].split(".")[1]))); + sessionStorage.setItem("userData", JSON.stringify(atob(res[1].split(".")[1]))); + sessionStorage.setItem("userToken", JSON.stringify(res[1])); + sessionStorage.setItem("isLogin", true); + setIsLogin(true); + alert("로그인 되었습니다!"); + return { type: "jwt", result: res[1] }; + } + } catch {} + }; + return ( + + {children} + + ); }; diff --git a/src/service/authentication/authentication.service.tsx b/src/service/authentication/authentication.service.tsx index 7e385c3..f8619a6 100644 --- a/src/service/authentication/authentication.service.tsx +++ b/src/service/authentication/authentication.service.tsx @@ -1,100 +1,119 @@ import axios, { AxiosError, AxiosResponse } from "axios"; -const CLIENT_ID = process.env.REACT_APP_CLIENT_ID; -const USER_API_URL = process.env.REACT_APP_USER_API_URL; - -// 깃허브 로그인창으로 다이렉트 해주는 함수 -export const GitHubLoginRequestHandler = () => { - // TODO: GitHub로부터 사용자 인증을 위해 GitHub로 이동해야 합니다. 적절한 URL을 입력하세요. - // OAuth 인증이 완료되면 authorization code와 함께 callback url로 리디렉션 합니다. - return window.location.assign(`https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}`); - //로그인 요청을 보내면 github auth server에서 redirect 로 callback, 그리고 auth code를 전달 -}; +const API_URL = process.env.REACT_APP_ALOG_API_URL; // EMAIL export const PostVerifyEmail = (email: string, code: string) => { - const verifyData = { - email: email, - code: code, - }; - console.log(verifyData); - const signUpResult: Promise = axios - .post(`${USER_API_URL}/api/users/emails/verify`, verifyData) - .then((res: AxiosResponse) => { - return res; - }) - .catch((err: AxiosError) => { - throw err; - }); - return signUpResult; + const verifyData = { + email: email, + code: code, + }; + console.log(verifyData); + const signUpResult: Promise = axios + .post(`${API_URL}/api/users/permit-all/emails/verify`, verifyData) + .then((res: AxiosResponse) => { + return res; + }) + .catch((err: AxiosError) => { + throw err; + }); + return signUpResult; }; export const PostSendVerifyEmail = (email: string) => { - const emailString = email.replace("@", "%40"); - const sendResult: Promise = axios - .post(`${USER_API_URL}/api/users/emails/send?EmailTo=${emailString}`) - .then((res: AxiosResponse) => { - return res; - }) - .catch((err: AxiosError) => { - throw err; - }); - return sendResult; + const emailString = email.replace("@", "%40"); + const sendResult: Promise = axios + .post(`${API_URL}/api/users/permit-all/emails/send?EmailTo=${emailString}`) + .then((res: AxiosResponse) => { + return res; + }) + .catch((err: AxiosError) => { + throw err; + }); + return sendResult; }; // USER AUTH export const UsersSignup = (email: string, userPw: string, userNN: string) => { - const SignUpData = { - userPw: userPw, - userNN: userNN, - email: email, - }; - const signUpResult: Promise = axios - .post(`${USER_API_URL}/api/users/signup`, SignUpData) - .then((res: AxiosResponse) => { - return res; - }) - .catch((err: AxiosError) => { - throw err; - }); - return signUpResult; + const SignUpData = { + userPw: userPw, + userNN: userNN, + email: email, + }; + const signUpResult: Promise = axios + // .post(`${API_URL}/api/users/signup`, SignUpData) + .post(`${API_URL}/api/users/permit-all/signup`, SignUpData) + .then((res: AxiosResponse) => { + return res; + }) + .catch((err: AxiosError) => { + throw err; + }); + return signUpResult; }; export const UsersLogin = (userEmail: string, userPassword: string): Promise => { - const loginData = { - userEmail: userEmail, - userPw: userPassword, - }; + const loginData = { + userEmail: userEmail, + userPw: userPassword, + }; - const loginResult: Promise = axios - .post(`${USER_API_URL}/api/users/login`, loginData) - .then((res: AxiosResponse) => { - return res; - }) - .catch((err: AxiosError) => { - throw err; - }); - return loginResult; + const loginResult: Promise = axios + .post(`${API_URL}/auth/permit-all/login`, loginData) + .then((res: AxiosResponse) => { + return res; + }) + .catch((err: AxiosError) => { + throw err; + }); + return loginResult; }; export const UsersInfo = () => { - return null; + return null; }; export const UsersCheckDuplicate = (userNN: string) => { - const checkDupResult: Promise = axios - .get(`${USER_API_URL}/api/users/duplicated/${userNN}`) - .then((res: AxiosResponse) => { - return res; - }) - .catch((err: AxiosError) => { - throw err; - }); - return checkDupResult; + const checkDupResult: Promise = axios + .get(`${API_URL}/api/users/permit-all/duplicated/${userNN}`) + .then((res: AxiosResponse) => { + return res; + }) + .catch((err: AxiosError) => { + throw err; + }); + return checkDupResult; }; export const UsersDelete = () => { - return null; + return null; +}; + +export const GetEmailWithGHToken = (accessToken: string) => { + const res: Promise = axios + .get(`${API_URL}/auth/permit-all/github/access-token?accessToken=${accessToken}`) + .then((res: AxiosResponse) => { + return res.data; + }) + .catch((err: AxiosError) => { + throw err; + }); + return res; +}; +export const PermitAllEmailLogin = (email: string) => { + const params = { + email: email, + }; + const res = axios + .get(`${API_URL}/auth/permit-all/email-login`, { params }) + .then((response) => { + console.log(response); + return response; + }) + .catch((error) => { + console.error(error); + }); + return res; }; diff --git a/src/service/authentication/github.service.tsx b/src/service/authentication/github.service.tsx new file mode 100644 index 0000000..d9fd24c --- /dev/null +++ b/src/service/authentication/github.service.tsx @@ -0,0 +1,53 @@ +import React from "react"; +import axios from "axios"; + +const CLIENT_ID = process.env.REACT_APP_CLIENT_ID!; +const CLIENT_SECRET = process.env.REACT_APP_CLIENT_SECRET!; + +const GITHUB_AUTH_CODE_SERVER = "/login/oauth/authorize"; +const GITHUB_AUTH_TOKEN_SERVER = "/login/oauth/access_token"; +const GITHUB_API_SERVER = "/user"; + +// 깃허브 로그인창으로 다이렉트 해주는 함수 +export const GitHubLoginRequestHandler = () => { + // TODO: GitHub로부터 사용자 인증을 위해 GitHub로 이동해야 합니다. 적절한 URL을 입력하세요. + // OAuth 인증이 완료되면 authorization code와 함께 callback url로 리디렉션 합니다. + return window.location.assign(`https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}`); + //로그인 요청을 보내면 github auth server에서 redirect 로 callback, 그리고 auth code를 전달 +}; + +export const GithubAuth = async (code: string) => { + console.log(code); + const clientId = CLIENT_ID; + const clientSecret = CLIENT_SECRET; + + const body = new URLSearchParams({ + client_id: clientId, + client_secret: clientSecret, + code: code, + }); + + // 요청 헤더를 설정합니다. + const headers = { + Accept: "application/json", + "Content-Type": "application/x-www-form-urlencoded", + }; + + // axios.post 메소드를 사용하여 요청을 보냅니다. + const res = await axios + .post(`${GITHUB_AUTH_TOKEN_SERVER}`, body, { headers: headers }) + .then((response) => { + // 응답을 로그로 출력합니다. + console.log("response : ", response.data); + // 응답 데이터에서 access_token을 추출합니다. + const accessToken = response.data.access_token; + // 필요한 경우 추가 로직을 여기에 추가할 수 있습니다. + + return accessToken; + }) + .catch((error) => { + // 에러를 로그로 출력합니다. + console.error("error : ", error.message); + }); + return res; +}; diff --git a/src/setupProxy.js b/src/setupProxy.js new file mode 100644 index 0000000..4257182 --- /dev/null +++ b/src/setupProxy.js @@ -0,0 +1,18 @@ +const { createProxyMiddleware } = require("http-proxy-middleware"); + +// src/setupProxy.js + +module.exports = function (app) { + app.use( + createProxyMiddleware("/login", { + target: "https://github.com", + changeOrigin: true, + }) + ); + app.use( + createProxyMiddleware("/user", { + target: "https://api.github.com", + changeOrigin: true, + }) + ); +}; diff --git a/tsconfig.json b/tsconfig.json index 9d379a3..139d543 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,24 @@ { +<<<<<<< HEAD + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"] +======= "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], @@ -17,4 +37,5 @@ "jsx": "react-jsx" }, "include": ["src"] +>>>>>>> 00b1bc3285f2d374a1b302a09a08a7d7255b3b16 }