From ca27932eea49d283257f51c375fa15e10aeb944f Mon Sep 17 00:00:00 2001 From: sumi-0011 Date: Thu, 30 May 2024 23:33:34 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=20cookie=EC=97=90=20role=EC=A0=80=EC=9E=A5,=20middlew?= =?UTF-8?q?are=EC=97=90=EC=84=9C=20=EC=A0=91=EA=B7=BC=EC=A0=9C=EC=96=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/cookie.ts | 1 + src/features/login/CertifyStep.tsx | 4 ++ src/features/login/JoinCompleteStep.tsx | 10 +++- src/hooks/apis/user/useGetInfo.ts | 23 ++++++++- src/middleware.ts | 7 +++ src/pages/admin/attendance.tsx | 69 ------------------------- 6 files changed, 42 insertions(+), 72 deletions(-) diff --git a/src/constants/cookie.ts b/src/constants/cookie.ts index 9d0d861..4c84d44 100644 --- a/src/constants/cookie.ts +++ b/src/constants/cookie.ts @@ -1,4 +1,5 @@ export const COOKIE_KEY = { ACCESS_TOKEN: 'depmat', REFRESH_TOKEN: 'depmrt', + USER_ROLE: 'depmur', }; diff --git a/src/features/login/CertifyStep.tsx b/src/features/login/CertifyStep.tsx index 7e61cf5..5ea11ab 100644 --- a/src/features/login/CertifyStep.tsx +++ b/src/features/login/CertifyStep.tsx @@ -1,6 +1,8 @@ import { useState } from 'react'; import { useRouter } from 'next/router'; +import Cookies from 'js-cookie'; +import { COOKIE_KEY } from '@/constants/cookie'; import { usePostLogin } from '@/hooks/apis/auth/usePostLogin'; import { getUserRoleByToken } from '@/hooks/apis/user/useGetInfo'; @@ -24,6 +26,8 @@ function CertifyStep(props: Props) { const { mutate } = usePostLogin({ onSuccess: async ({ accessToken }) => { const role = await getUserRoleByToken(accessToken); + Cookies.set(COOKIE_KEY.USER_ROLE, role, { expires: 1 }); + if (role === 'ORGANIZER') { router.replace('/admin/attendance'); } else { diff --git a/src/features/login/JoinCompleteStep.tsx b/src/features/login/JoinCompleteStep.tsx index 20e978c..2c817f1 100644 --- a/src/features/login/JoinCompleteStep.tsx +++ b/src/features/login/JoinCompleteStep.tsx @@ -1,7 +1,9 @@ import Image from 'next/image'; import { useRouter } from 'next/router'; +import Cookies from 'js-cookie'; import styled from 'styled-components'; +import { COOKIE_KEY } from '@/constants/cookie'; import { useGetInfo } from '@/hooks/apis/user/useGetInfo'; import LoginLayout from './LoginLayout'; @@ -12,7 +14,13 @@ function JoinCompleteStep() { const { data } = useGetInfo(); const onNext = () => { - if (data?.generations[0].role === 'ORGANIZER') { + const role = data?.generations[0].role; + + if (role) { + Cookies.set(COOKIE_KEY.USER_ROLE, role, { expires: 1 }); + } + + if (role === 'ORGANIZER') { router.replace('/admin/attendance'); } else { router.replace('/'); diff --git a/src/hooks/apis/user/useGetInfo.ts b/src/hooks/apis/user/useGetInfo.ts index 07f1804..e4edf9b 100644 --- a/src/hooks/apis/user/useGetInfo.ts +++ b/src/hooks/apis/user/useGetInfo.ts @@ -4,6 +4,8 @@ import { useQuery } from '@tanstack/react-query'; import type { CustomError } from '@/apis'; import { api } from '@/apis'; +const GET_INFO_URL = '/v1/me'; + type Role = 'ORGANIZER' | 'MEMBER'; interface GetInfoResponse { @@ -18,7 +20,7 @@ interface GetInfoResponse { } export const getInfoByToken = async (token: string): Promise => { - return await api.get('/v1/me', { + return await api.get(GET_INFO_URL, { headers: { Authorization: `Bearer ${token}`, }, @@ -31,7 +33,7 @@ export const getUserRoleByToken = async (token: string): Promise => { return generations[0].role; }; -const getInfo = () => api.get('/v1/me'); +export const getInfo = () => api.get(GET_INFO_URL); export const useGetInfo = (options?: UseQueryOptions) => useQuery({ @@ -39,3 +41,20 @@ export const useGetInfo = (options?: UseQueryOptions getInfo(), ...options, }); + +// export const fetchUserInfo = () => fetch(GET_INFO_URL) +export const fetchUserInfo = async (accessToken: string) => { + const response = await fetch(process.env.NEXT_PUBLIC_API_URL + '/v1/me', { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + const data = await response.json(); + + return data; +}; + +export const fetchUserRole = async (accessToken: string): Promise => { + const data = await fetchUserInfo(accessToken); + return data.generations[0].role; +}; diff --git a/src/middleware.ts b/src/middleware.ts index 21bf8dd..96c18c2 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -25,6 +25,13 @@ export async function middleware(request: NextRequest) { } } + if (request.nextUrl.pathname.startsWith('/admin')) { + const role = request.cookies.get(COOKIE_KEY.USER_ROLE)?.value; + if (role !== 'ORGANIZER') { + return NextResponse.redirect(new URL('/', request.url)); + } + } + return response; } diff --git a/src/pages/admin/attendance.tsx b/src/pages/admin/attendance.tsx index e768030..d7daab8 100644 --- a/src/pages/admin/attendance.tsx +++ b/src/pages/admin/attendance.tsx @@ -5,7 +5,6 @@ import styled from 'styled-components'; import { BottomNav } from '@/components/BottomNav'; import IconButton from '@/components/Button/IconButton'; import Layout from '@/components/Layout'; -import { ATTENDANCE_STATUS } from '@/constants/attendance'; import { ADMIN_NAV_ITEMS } from '@/constants/bottomNav'; import TeamSelect from '@/features/admin/attendance/TeamSelect'; import UserItem from '@/features/admin/attendance/UserItem'; @@ -125,71 +124,3 @@ const UserSection = styled.section` border-top: 1px solid ${({ theme }) => theme.color.gray_200}; } `; - -const DUMMY_DATA: { - id: number; - name: string; - position: string; - status: ATTENDANCE_STATUS; -}[] = [ - { - id: 1, - name: '김민수', - position: '개발자', - status: ATTENDANCE_STATUS.출석대기, - }, - { - id: 2, - name: '홍길동', - position: '디자이너', - status: ATTENDANCE_STATUS.지각, - }, - { - id: 3, - name: '이영희', - position: '디자이너', - status: ATTENDANCE_STATUS.출석, - }, - { - id: 4, - name: '박철수', - position: '디자이너', - status: ATTENDANCE_STATUS.결석, - }, - { - id: 5, - name: '김지영', - position: '디자이너', - status: ATTENDANCE_STATUS.출석대기, - }, - { - id: 6, - name: '이승호', - position: '디자이너', - status: ATTENDANCE_STATUS.출석대기, - }, - { - id: 7, - name: '박지민', - position: '디자이너', - status: ATTENDANCE_STATUS.출석대기, - }, - { - id: 8, - name: '박지민', - position: '디자이너', - status: ATTENDANCE_STATUS.출석대기, - }, - { - id: 9, - name: '박지민', - position: '디자이너', - status: ATTENDANCE_STATUS.출석대기, - }, - { - id: 10, - name: '박지민', - position: '디자이너', - status: ATTENDANCE_STATUS.출석대기, - }, -]; From 2b974c425b3cb48b8a34abb769e827c9f2eb9b5d Mon Sep 17 00:00:00 2001 From: sumi-0011 Date: Mon, 3 Jun 2024 15:36:21 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20role=20cookie=EC=97=90=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기수 정보도 같이 저장 --- src/constants/cookie.ts | 1 + src/features/login/CertifyStep.tsx | 6 +++--- src/features/login/JoinCompleteStep.tsx | 8 ++++---- src/hooks/apis/auth/usePostLogin.ts | 5 +++++ src/hooks/apis/user/useGetInfo.ts | 14 ++------------ src/hooks/apis/user/user.d.ts | 12 ++++++++++++ 6 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 src/hooks/apis/user/user.d.ts diff --git a/src/constants/cookie.ts b/src/constants/cookie.ts index 9d0d861..8d1febb 100644 --- a/src/constants/cookie.ts +++ b/src/constants/cookie.ts @@ -1,4 +1,5 @@ export const COOKIE_KEY = { ACCESS_TOKEN: 'depmat', REFRESH_TOKEN: 'depmrt', + ROLE: 'depmrole', }; diff --git a/src/features/login/CertifyStep.tsx b/src/features/login/CertifyStep.tsx index 7e61cf5..ce785d2 100644 --- a/src/features/login/CertifyStep.tsx +++ b/src/features/login/CertifyStep.tsx @@ -2,7 +2,6 @@ import { useState } from 'react'; import { useRouter } from 'next/router'; import { usePostLogin } from '@/hooks/apis/auth/usePostLogin'; -import { getUserRoleByToken } from '@/hooks/apis/user/useGetInfo'; import LoginLayout from './LoginLayout'; import PasswordInput from './PasswordInput'; @@ -22,8 +21,9 @@ function CertifyStep(props: Props) { const isDisabled = value.length !== PASSWORD_LENGTH || Boolean(error); const { mutate } = usePostLogin({ - onSuccess: async ({ accessToken }) => { - const role = await getUserRoleByToken(accessToken); + onSuccess: async (data) => { + const role = data.member.generations[0].role; + if (role === 'ORGANIZER') { router.replace('/admin/attendance'); } else { diff --git a/src/features/login/JoinCompleteStep.tsx b/src/features/login/JoinCompleteStep.tsx index 20e978c..e75ef86 100644 --- a/src/features/login/JoinCompleteStep.tsx +++ b/src/features/login/JoinCompleteStep.tsx @@ -1,18 +1,18 @@ import Image from 'next/image'; import { useRouter } from 'next/router'; +import Cookies from 'js-cookie'; import styled from 'styled-components'; -import { useGetInfo } from '@/hooks/apis/user/useGetInfo'; - import LoginLayout from './LoginLayout'; import Img from './welcome-2.png'; function JoinCompleteStep() { const router = useRouter(); - const { data } = useGetInfo(); const onNext = () => { - if (data?.generations[0].role === 'ORGANIZER') { + const roleData = Cookies.get('role'); + + if (roleData?.includes('ORGANIZER')) { router.replace('/admin/attendance'); } else { router.replace('/'); diff --git a/src/hooks/apis/auth/usePostLogin.ts b/src/hooks/apis/auth/usePostLogin.ts index 5291885..cf14cf6 100644 --- a/src/hooks/apis/auth/usePostLogin.ts +++ b/src/hooks/apis/auth/usePostLogin.ts @@ -5,6 +5,7 @@ import Cookies from 'js-cookie'; import type { CustomError } from '@/apis'; import { api } from '@/apis'; import { COOKIE_KEY } from '@/constants/cookie'; +import type { UserInfo } from '@/hooks/apis/user/user'; interface PostLoginRequest { email: string; @@ -14,6 +15,7 @@ interface PostLoginRequest { interface PostLoginResponse { accessToken: string; refreshToken: string; + member: UserInfo; } const postLogin = async (request: PostLoginRequest): Promise => { @@ -27,6 +29,9 @@ export const usePostLogin = (options?: UseMutationOptions { Cookies.set(COOKIE_KEY.ACCESS_TOKEN, data.accessToken, { expires: 1 }); Cookies.set(COOKIE_KEY.REFRESH_TOKEN, data.refreshToken, { expires: 7 }); + Cookies.set(COOKIE_KEY.ROLE, `${data.member.generations[0].generationId}-${data.member.generations[0].role}`, { + expires: 1, + }); options?.onSuccess?.({ ...data }, ...rest); }, diff --git a/src/hooks/apis/user/useGetInfo.ts b/src/hooks/apis/user/useGetInfo.ts index 07f1804..ca23f9b 100644 --- a/src/hooks/apis/user/useGetInfo.ts +++ b/src/hooks/apis/user/useGetInfo.ts @@ -4,19 +4,9 @@ import { useQuery } from '@tanstack/react-query'; import type { CustomError } from '@/apis'; import { api } from '@/apis'; -type Role = 'ORGANIZER' | 'MEMBER'; - -interface GetInfoResponse { - id: string; - name: string; - email: string; - generations: { - generationId: number; - role: Role; - position: string; - }[]; -} +import type { Role, UserInfo } from './user'; +type GetInfoResponse = UserInfo; export const getInfoByToken = async (token: string): Promise => { return await api.get('/v1/me', { headers: { diff --git a/src/hooks/apis/user/user.d.ts b/src/hooks/apis/user/user.d.ts new file mode 100644 index 0000000..5e77923 --- /dev/null +++ b/src/hooks/apis/user/user.d.ts @@ -0,0 +1,12 @@ +export type Role = 'ORGANIZER' | 'MEMBER'; + +export interface UserInfo { + id: string; + name: string; + email: string; + generations: { + generationId: number; + role: Role; + position: string; + }[]; +} From fa41d31154537465033a4301612dce5098d412e2 Mon Sep 17 00:00:00 2001 From: sumi-0011 Date: Mon, 3 Jun 2024 15:46:24 +0900 Subject: [PATCH 3/3] fix: merge confilc solve --- src/features/admin/attendance/UserItem.tsx | 1 - src/middleware.ts | 4 ++-- src/pages/index.tsx | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/features/admin/attendance/UserItem.tsx b/src/features/admin/attendance/UserItem.tsx index b25f6fe..ed1bd0d 100644 --- a/src/features/admin/attendance/UserItem.tsx +++ b/src/features/admin/attendance/UserItem.tsx @@ -14,7 +14,6 @@ function UserItem(props: AttendanceItemType) { const onChange = (value: ATTENDANCE_STATUS) => { // optimistic update setStatus(value); - console.log('value: ', value); // api call mutate({ attendanceId: props.attendanceId, attendanceStatus: value }); diff --git a/src/middleware.ts b/src/middleware.ts index 96c18c2..a5dd693 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -26,8 +26,8 @@ export async function middleware(request: NextRequest) { } if (request.nextUrl.pathname.startsWith('/admin')) { - const role = request.cookies.get(COOKIE_KEY.USER_ROLE)?.value; - if (role !== 'ORGANIZER') { + const role = request.cookies.get(COOKIE_KEY.ROLE)?.value; + if (!role?.includes('ORGANIZER')) { return NextResponse.redirect(new URL('/', request.url)); } } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 79c7086..ce08957 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -52,7 +52,6 @@ const Home = () => { // NOTE: 유저 정보 가져오기 const { data } = useGetInfo(); - console.log('data: ', data); const handleClickCheckIn = () => { mutate();