-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use info logic/#82 #86
Changes from all commits
0f60da0
ae40793
944399f
4e06500
f6a2f42
1ccb7b3
5863c3b
f125a64
804fa95
d4e50cc
eca405f
8bbc2f5
259ed10
364e312
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import UserDeleteModal from '@/app/ui/user/user-info-navigator/user-delete-modal'; | ||
|
||
export default function FloatingComponentContainer() { | ||
return ( | ||
<> | ||
<UserDeleteModal /> | ||
</> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import '@testing-library/jest-dom'; | ||
import UserInfoNavigator from '@/app/ui/user/user-info-navigator/user-info-navigator'; | ||
import { render, screen } from '@testing-library/react'; | ||
|
||
jest.mock('next/headers', () => ({ | ||
cookies: jest.fn().mockReturnValue({ | ||
get: jest.fn().mockReturnValue({ | ||
value: 'fake-access-token', | ||
}), | ||
}), | ||
})); | ||
|
||
describe('UserInfoNavigator', () => { | ||
it('UserInfoNavigator๋ฅผ ๋ ๋๋งํ๋ค.', async () => { | ||
render(await UserInfoNavigator()); | ||
|
||
expect(await screen.findByText(/๋ชจํน์ด/i)).toBeInTheDocument(); | ||
expect(await screen.findByText(/์ตํฉ์ํํธ์จ์ด/i)).toBeInTheDocument(); | ||
expect(await screen.findByText(/60000000/i)).toBeInTheDocument(); | ||
}); | ||
}); |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. user ํ์ผ์ด ํ์คํ ๊ธธ์ด์ง๊ธด ํ๋ค์ user๋ auth ํ์ผ์ ๋ถ๋ฆฌํ์๋ ๊ฑด ์ด๋ค๊ฐ์? |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
|
||
import { FormState } from '@/app/ui/view/molecule/form/form-root'; | ||
import { API_PATH } from '../api-path'; | ||
import { SignUpRequestBody, SignInRequestBody, ValidateTokenResponse } from './user.type'; | ||
import { SignUpRequestBody, SignInRequestBody, ValidateTokenResponse, UserDeleteRequestBody } from './user.type'; | ||
import { httpErrorHandler } from '@/app/utils/http/http-error-handler'; | ||
import { BadRequestError } from '@/app/utils/http/http-error'; | ||
import { | ||
|
@@ -15,6 +15,53 @@ import { cookies } from 'next/headers'; | |
import { isValidation } from '@/app/utils/zod/validation.util'; | ||
import { redirect } from 'next/navigation'; | ||
|
||
export async function signOut() { | ||
cookies().delete('accessToken'); | ||
cookies().delete('refreshToken'); | ||
|
||
redirect('/sign-in'); | ||
} | ||
Comment on lines
+18
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. async ํค์๋๊ฐ ์กด์ฌํ๋๋ฐ ํน์ ์ด์ ๊ฐ ์์๊น์? |
||
|
||
export async function deleteUser(prevState: FormState, formData: FormData): Promise<FormState> { | ||
try { | ||
const body: UserDeleteRequestBody = { | ||
password: formData.get('password') as string, | ||
}; | ||
|
||
const response = await fetch(`${API_PATH.user}/delete-me`, { | ||
method: 'DELETE', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Bearer ${cookies().get('accessToken')?.value}`, | ||
}, | ||
body: JSON.stringify(body), | ||
}); | ||
const result = await response.json(); | ||
|
||
httpErrorHandler(response, result); | ||
} catch (error) { | ||
if (error instanceof BadRequestError) { | ||
// ์๋ชป๋ ์์ฒญ ์ฒ๋ฆฌ ๋ก์ง | ||
return { | ||
isSuccess: false, | ||
isFailure: true, | ||
validationError: {}, | ||
message: error.message, | ||
}; | ||
} else { | ||
// ๋๋จธ์ง ์๋ฌ๋ ๋ ์์ ์์ค์์ ์ฒ๋ฆฌ | ||
throw error; | ||
} | ||
} | ||
|
||
return { | ||
isSuccess: true, | ||
isFailure: false, | ||
validationError: {}, | ||
message: 'ํ์ ํํด๊ฐ ์๋ฃ๋์์ต๋๋ค.', | ||
}; | ||
} | ||
|
||
export async function validateToken(): Promise<ValidateTokenResponse | false> { | ||
const accessToken = cookies().get('accessToken')?.value; | ||
const refreshToken = cookies().get('refreshToken')?.value; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
'use client'; | ||
import { signOut } from '@/app/business/user/user.command'; | ||
import Button from '../../view/atom/button/button'; | ||
|
||
export default function SignOutButton() { | ||
const handleSignOut = async () => { | ||
await signOut(); | ||
}; | ||
|
||
return <Button onClick={handleSignOut} size="sm" variant="secondary" label="๋ก๊ทธ์์" />; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
'use client'; | ||
import Button from '../../view/atom/button/button'; | ||
import { DIALOG_KEY } from '@/app/utils/key/dialog.key'; | ||
import useDialog from '@/app/hooks/useDialog'; | ||
|
||
export default function UserDeleteButton() { | ||
const { toggle } = useDialog(DIALOG_KEY.USER_DEELETE); | ||
|
||
const handleModalToggle = () => { | ||
toggle(); | ||
}; | ||
|
||
return <Button onClick={handleModalToggle} size="sm" variant="text" label="ํ์ํํดํ๊ธฐ" />; | ||
Comment on lines
+9
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. onClick = {toggle} ๋ก ์ ์ ์๋ ์๋๋ฐ ๋ฐ๋ก handleModalToggle ํจ์๋ฅผ ๋ง๋์ ์ด์ ๊ฐ ์์๊น์? |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
'use client'; | ||
import { DIALOG_KEY } from '@/app/utils/key/dialog.key'; | ||
import Modal from '../../view/molecule/modal/modal'; | ||
import Form from '../../view/molecule/form'; | ||
import { deleteUser } from '@/app/business/user/user.command'; | ||
|
||
export default function UserDeleteModal() { | ||
return ( | ||
<Modal modalKey={DIALOG_KEY.USER_DEELETE}> | ||
<div className="max-w-sm mx-auto my-12 p-6 border rounded-lg shadow-md bg-white"> | ||
<h2 className="text-xl font-bold text-center">ํ์ ํํด</h2> | ||
<div className="h-1 w-10 bg-blue-600 mx-auto my-3" /> | ||
<p className="text-sm text-center my-4"> | ||
ํ์ํํด๋ฅผ ์งํํ์๊ฒ ์ต๋๊น? ํํด๋ฅผ ์งํํ๋ฉด๋ ๋น๋ฐ๋ฒํธ ์ ๋ ฅ์ด ํ์ํฉ๋๋ค. | ||
</p> | ||
<div className="my-4"> | ||
<Form failMessageControl={'toast'} action={deleteUser} id={'user-delete'}> | ||
<Form.PasswordInput label="๋น๋ฐ๋ฒํธ" id="password" placeholder="๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํ์ธ์" required={true} /> | ||
<Form.SubmitButton label="ํํดํ๊ธฐ" position="center" variant="primary" /> | ||
</Form> | ||
</div> | ||
<p className="text-xs text-center my-4">์ ๋ณด๋ฅผ ๋๋ฝํ์ฌ ์๋น์ค๋ฅผ ์ด์ฉํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.</p> | ||
</div> | ||
</Modal> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2,6 +2,7 @@ export const DIALOG_KEY = { | |||||
RESULT_CATEGORY: 'RESULT_CATEGORY', | ||||||
DIALOG_TEST: 'DIALOG_TEST', | ||||||
LECTURE_SEARCH: 'LECTURE_SEARCH', | ||||||
USER_DEELETE: 'USER_DEELETE', | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} as const; | ||||||
|
||||||
export type DialogKey = (typeof DIALOG_KEY)[keyof typeof DIALOG_KEY]; |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -31,10 +31,12 @@ async function getAuth(request: NextRequest): Promise<{ | |||||||||
}; | ||||||||||
} | ||||||||||
|
||||||||||
const allowdGuestPath = ['/tutorial', '/sign-in', '/sign-up', '/find-password', '/find-id']; | ||||||||||
const allowdOnlyGuestPath = ['/sign-in', '/sign-up', '/find-password', '/find-id']; | ||||||||||
const allowdGuestPath = ['/', '/tutorial', ...allowdOnlyGuestPath]; | ||||||||||
|
||||||||||
function isAllowedGuestPath(path: string) { | ||||||||||
return allowdGuestPath.some((allowedPath) => path.startsWith(allowedPath)); | ||||||||||
function isAllowedGuestPath(path: string, strict: boolean = false) { | ||||||||||
const allowdPath = strict ? allowdOnlyGuestPath : allowdGuestPath; | ||||||||||
return allowdPath.some((allowedPath) => path.startsWith(allowedPath)); | ||||||||||
Comment on lines
+38
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
|
||||||||||
export async function middleware(request: NextRequest) { | ||||||||||
|
@@ -51,6 +53,10 @@ export async function middleware(request: NextRequest) { | |||||||||
if (auth.role === 'guest' && !isAllowedGuestPath(request.nextUrl.pathname)) { | ||||||||||
return Response.redirect(new URL('/sign-in', request.url)); | ||||||||||
} | ||||||||||
|
||||||||||
if (auth.role !== 'guest' && isAllowedGuestPath(request.nextUrl.pathname, true)) { | ||||||||||
return Response.redirect(new URL('/my', request.url)); | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
์ด ์ปดํฌ๋ํธ๋ฅผ ๊ตฌํํ์ ์ด์ ๊ฐ ์์๊น์?