-
Notifications
You must be signed in to change notification settings - Fork 2
API 키 관리 기능 추가 및 코드 리팩토링 #3
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
base: dev
Are you sure you want to change the base?
Changes from 19 commits
03dabfa
dd823ee
8467893
e788019
fc6c0f8
c3a0638
c9b4587
82cabba
cdaae14
1cbe0ac
272b8c3
48af003
d6a64d1
96957cd
078ce62
202f3f8
09c0eb5
ea2a521
060e5e4
fbbb68c
f0201cd
b42895b
eebc5bb
9738bfb
a6bfe71
58c86ee
2e88e70
28bb5b1
dc73a5b
2a954e4
bc39cb8
45dd7aa
930fbb1
eddbfe2
98dab57
20cea3e
273095b
afafae0
e5a1046
62a6558
425848f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import * as LucideIcons from 'lucide-react'; | ||
|
|
||
| const IconComponnt = ({ icon }: { icon: keyof typeof LucideIcons }) => { | ||
| const LucideIcon = LucideIcons[icon] as LucideIcons.LucideIcon; | ||
| return <LucideIcon />; | ||
| }; | ||
|
|
||
| export default IconComponnt; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { Link } from 'react-router-dom'; | ||
|
|
||
| const ApiKeyInfo = () => { | ||
| return ( | ||
| <div className='mb-8'> | ||
| <p className='text-center text-light-40 text-sm mb-4'> | ||
| 한 계정당 키는 한개만 발급가능합니다 | ||
| </p> | ||
| <p className='text-center text-light-40 text-sm mb-4'> | ||
| 갱신하면 새로운 키가 발급되어 기존의 키는 사용할 수 없습니다 | ||
| </p> | ||
| <Link | ||
| to={'https://github.com/FC-InnerCircle-ICD2/fintech-FE-SDK'} | ||
| target='_blank' | ||
| className='block text-center text-primary hover:underline' | ||
| > | ||
| API 사용방법 | ||
| </Link> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default ApiKeyInfo; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { Button } from '@components/ui/button'; | ||
| import Info from './Info'; | ||
| import Separator from './Separator'; | ||
| import { createToastSuccess } from '@lib/toast'; | ||
| import { useKeys } from '@hooks/api/useKeys'; | ||
| import { useAuthStore } from '@store/authStore'; | ||
| import { useState } from 'react'; | ||
|
|
||
| const Issued = ({ issuedApiKey }: { issuedApiKey: string }) => { | ||
| const [apiKey, setApiKey] = useState<string>(issuedApiKey); | ||
| const { mutate: renewKey } = useKeys(); | ||
| const { auth } = useAuthStore(); | ||
|
|
||
| const handleCopy = () => { | ||
| const apiKey = document.getElementById('api-key')!.textContent!; | ||
| navigator.clipboard.writeText(apiKey).then(() => { | ||
| createToastSuccess('', 'API 키를 복사하였습니다.'); | ||
| }); | ||
| }; | ||
|
|
||
| const handleRenew = () => { | ||
| renewKey( | ||
| { id: auth!.id }, | ||
| { | ||
| onSuccess: ({ data }) => { | ||
| setApiKey(data.apiKey); | ||
| createToastSuccess('', 'API 키가 성공적으로 갱신되었습니다.'); | ||
| }, | ||
| }, | ||
| ); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className='bg-white rounded-3xl shadow-lg p-24 flex flex-col items-center'> | ||
| <h1 | ||
| className='text-2xl font-bold text-primary text-center mb-4' | ||
| id='api-key' | ||
| > | ||
| {apiKey} | ||
| </h1> | ||
|
|
||
| <p | ||
| className='mb-4 text-xs text-primary cursor-pointer hover:underline' | ||
| onClick={handleCopy} | ||
| > | ||
| 복사하기 | ||
| </p> | ||
|
|
||
| <Separator /> | ||
|
|
||
| <Info /> | ||
|
|
||
| <Separator /> | ||
|
|
||
| <Button size={'rounded'} onClick={handleRenew}> | ||
| 갱신하기 | ||
| </Button> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default Issued; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import { Button } from '@components/ui/button'; | ||
| import Info from './Info'; | ||
| import { useAuthStore } from '@store/authStore'; | ||
| import { useKeys } from '@hooks/api/useKeys'; | ||
| import { createToastSuccess } from '@lib/toast'; | ||
|
|
||
| const NotIssued = ({ | ||
| onIssueSuccess, | ||
| }: { | ||
| onIssueSuccess: (value: string) => void; | ||
| }) => { | ||
| const { auth } = useAuthStore(); | ||
| const { mutate: renewKey } = useKeys(); | ||
|
|
||
| const handleIssue = () => { | ||
| renewKey( | ||
| { id: auth!.id }, | ||
| { | ||
| onSuccess: ({ data }) => { | ||
| createToastSuccess('', 'API 키가 성공적으로 발급되었습니다.'); | ||
| onIssueSuccess(data.apiKey); | ||
| }, | ||
| }, | ||
| ); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className='bg-white rounded-3xl shadow-lg p-24 flex flex-col items-center'> | ||
| <h1 className='text-2xl font-bold text-primary text-center mb-10'> | ||
| 키 발급하기 | ||
| </h1> | ||
|
|
||
| <Info /> | ||
|
|
||
| <Button size={'rounded'} onClick={handleIssue}> | ||
| 발급하기 | ||
| </Button> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default NotIssued; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| const Separator = () => { | ||
| return ( | ||
| <div role='separator' className='w-full h-[1px] bg-light-border mb-8' /> | ||
| ); | ||
| }; | ||
|
|
||
| export default Separator; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,20 @@ | ||
| import { sideBarItems } from '@constants/sidebar.ts'; | ||
| import SideBarItem from './SideBarItem.tsx'; | ||
| import { useLocation } from 'react-router-dom'; | ||
|
|
||
| import { useAuthStore } from '@store/authStore'; | ||
| import IconComponnt from '../IconComponnt.tsx'; | ||
| const SideBar = () => { | ||
| const { pathname } = useLocation(); | ||
| const { auth, logout } = useAuthStore(); | ||
|
|
||
| return ( | ||
| <aside className='w-60 h-screen bg-[#f1f1f1] p-12'> | ||
| <figure className='flex flex-col items-center gap-2'> | ||
| <img src='/logo.png' className='w-8' /> | ||
| <h4 className='font-bold text-xl truncate'>Admin</h4> | ||
| <h4 className='font-bold text-xl truncate'>{auth?.name}</h4> | ||
| </figure> | ||
|
|
||
| <nav> | ||
| <nav className='flex flex-col h-[calc(100%-100px)]'> | ||
|
Collaborator
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. 아마 여기도
Collaborator
Author
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. 지난번에 알려주신대로 수정했는데 제가 의도한것과 다르게 동작합니다 |
||
| <ul> | ||
| {sideBarItems.map((item) => ( | ||
| <SideBarItem | ||
|
|
@@ -24,6 +26,15 @@ const SideBar = () => { | |
| /> | ||
| ))} | ||
| </ul> | ||
| <ul className='mt-auto px-4'> | ||
| <li | ||
| className='cursor-pointer text-center flex gap-2' | ||
| onClick={() => logout()} | ||
|
H-Genie marked this conversation as resolved.
|
||
| > | ||
| <IconComponnt icon='LogOut' /> | ||
| <p>로그아웃</p> | ||
| </li> | ||
| </ul> | ||
| </nav> | ||
| </aside> | ||
| ); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { useAuthStore } from '@store/authStore'; | ||
| import { Navigate, useLocation } from 'react-router-dom'; | ||
| import { ROUTES } from '@constants/routes'; | ||
| interface ProtectedRouteProps { | ||
| children: React.ReactNode; | ||
| } | ||
|
|
||
| export const ProtectedRoute = ({ children }: ProtectedRouteProps) => { | ||
| const { auth } = useAuthStore(); | ||
| const location = useLocation(); | ||
|
|
||
| if (!auth) { | ||
| return <Navigate to={ROUTES.LOGIN} state={{ from: location }} replace />; | ||
| } | ||
|
|
||
| return <>{children}</>; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| export const ROUTES = { | ||
| MAIN: '/', | ||
| LOGIN: '/login', | ||
| SIGNUP: '/signup', | ||
| TRANSACTIONS: '/transactions', | ||
| KEY_MANAGEMENT: '/key-management', | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { useMutation, useQuery } from '@tanstack/react-query'; | ||
| import { apiClient } from '@lib/api/apiClient'; | ||
| import { API_ENDPOINTS } from '@constants/apiEndpoints'; | ||
| import type { KeyReq, KeyRes } from '@type/key'; | ||
|
|
||
| export const useKeys = () => { | ||
| return useMutation({ | ||
| mutationFn: (data: KeyReq) => | ||
| apiClient.post<KeyReq, KeyRes>(API_ENDPOINTS.MANAGEMENT.KEYS, data), | ||
| }); | ||
| }; | ||
|
|
||
| export const useKeysId = (id: string) => { | ||
| return useQuery({ | ||
| queryKey: ['get-key'], | ||
| queryFn: () => apiClient.get<KeyRes>(API_ENDPOINTS.MANAGEMENT.ID(id)), | ||
| }); | ||
| }; |
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.
어짜피
apiKey를 상태값으로 저장하고 있는 것 아닌가요? 로그인 가능한 계정이 무엇인지 몰라서 테스트는 못해봤지만, 아마apiKey상태값의 초기값으로props가 들어가있고handleRenew에서 set하고 있어서apiKey상태값으로 복사가 가능해보이는데요!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.
만약 그게 아니라면 useRef로 가져올 수도 있을 것 같아요!
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.
React스럽게요!
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.
직접적으로 태그의 스타일을 변경하는 경우라면 useRef를 사용했겠지만 단순히 값만 읽어오는 것이기에 불필요한 동작이라고 생각합니다