-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from hy2n/master
관리자 기능 추가
- Loading branch information
Showing
12 changed files
with
567 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import React from 'react'; | ||
|
||
const LoadingFailSpinner = ({ message = '알 수 없는 문제가 발생했어요' }) => { | ||
return ( | ||
<div className="flex flex-col items-center justify-center h-[300px]"> | ||
<div className="relative w-20 h-20 mb-6"> | ||
<div className="absolute inset-0 bg-red-500 rounded-full"></div> | ||
<div className="absolute inset-0 flex items-center justify-center"> | ||
<svg className="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> | ||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /> | ||
</svg> | ||
</div> | ||
</div> | ||
<p className="text-lg font-medium text-red-600 mb-4">{message}</p> | ||
</div> | ||
); | ||
}; | ||
|
||
export default LoadingFailSpinner; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,21 @@ | ||
import React from 'react'; | ||
|
||
const Loader = ({ size = 'md' }) => { | ||
const LoadingSpinner = ({ size = 'md', message = '로딩 중이에요!' }) => { | ||
const sizeClasses = { | ||
sm: 'w-5 h-5 border-2', | ||
md: 'w-8 h-8 border-3', | ||
lg: 'w-12 h-12 border-4', | ||
sm: 'w-12 h-12', | ||
md: 'w-16 h-16', | ||
lg: 'w-20 h-20', | ||
}; | ||
|
||
return ( | ||
<div className="flex justify-center items-center"> | ||
<div | ||
className={`${sizeClasses[size]} border-t-blue-500 border-r-blue-500 border-b-blue-400 border-l-blue-200 rounded-full animate-spin`} | ||
></div> | ||
<div className="flex flex-col items-center justify-center h-[300px]"> | ||
<div className={`relative ${sizeClasses[size]}`}> | ||
<div className="absolute inset-0 bg-blue-500 rounded-full opacity-20"></div> | ||
<div className="absolute inset-0 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"></div> | ||
</div> | ||
<p className="mt-6 text-lg font-medium text-blue-600">{message}</p> | ||
</div> | ||
); | ||
}; | ||
|
||
export default function LoadingSpinner() { | ||
return ( | ||
<div className="flex flex-col items-center justify-center h-screen bg-gray-100"> | ||
<Loader size="lg" /> | ||
<p className="mt-4 text-gray-600 font-medium">🚗 로딩 중이에요!</p> | ||
</div> | ||
); | ||
} | ||
export default LoadingSpinner; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React from 'react'; | ||
import { Navigate, useLocation } from 'react-router-dom'; | ||
import AuthHook from '../hook/AuthHook'; | ||
import LoadingSpinner from './LoadingIcon'; | ||
|
||
const ProtectedRoute = ({ children }) => { | ||
const { isAuthenticated, isLoading } = AuthHook(); | ||
const location = useLocation(); | ||
|
||
if (isLoading) { | ||
return <LoadingSpinner />; | ||
} | ||
|
||
if (!isAuthenticated) { | ||
window.location.href = `https://id.nanu.cc/oauth?app_id=6b3bac76-bde8-4681-8c55-0e915d1c03b6&app_name=%EB%8F%99%ED%98%84%20%EA%B8%B0%EC%88%A0%20%EB%B8%94%EB%A1%9C%EA%B7%B8&redirect_uri=https://donghyun.cc/oauth_client`; | ||
return null; | ||
} | ||
|
||
return children; | ||
}; | ||
|
||
export default ProtectedRoute; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { useState, useEffect, useCallback } from 'react'; | ||
import OAuthSDK from '../nanuid_auth_sdk' | ||
|
||
const useAuthCheck = () => { | ||
const [isAuthenticated, setIsAuthenticated] = useState(false); | ||
const [isLoading, setIsLoading] = useState(true); | ||
const [token, setToken] = useState(null); | ||
|
||
const refreshTokenAndUpdate = useCallback(async () => { | ||
const newAccessToken = await OAuthSDK.refreshToken(); | ||
if (newAccessToken) { | ||
setToken(newAccessToken); | ||
setIsAuthenticated(true); | ||
} else { | ||
setToken(null); | ||
setIsAuthenticated(false); | ||
} | ||
return newAccessToken; | ||
}, []); | ||
|
||
useEffect(() => { | ||
const checkAuth = async () => { | ||
setIsLoading(true); | ||
const accessToken = OAuthSDK.getAccessToken(); | ||
|
||
if (accessToken) { | ||
const isExpired = await OAuthSDK.isTokenExpired(accessToken); | ||
if (isExpired) { | ||
await refreshTokenAndUpdate(); | ||
} else { | ||
setIsAuthenticated(true); | ||
setToken(accessToken); | ||
} | ||
} else { | ||
await refreshTokenAndUpdate(); | ||
} | ||
setIsLoading(false); | ||
}; | ||
|
||
checkAuth(); | ||
}, [refreshTokenAndUpdate]); | ||
|
||
return { isAuthenticated, isLoading, token, refreshTokenAndUpdate }; | ||
}; | ||
|
||
export default useAuthCheck; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import axios from 'axios'; | ||
const AUTH_BASE_URL = 'https://auth.nanu.cc'; | ||
|
||
export const OAuthSDK = { | ||
getAccessToken: () => { | ||
return document.cookie.replace(/(?:(?:^|.*;\s*)ACCESS\s*=\s*([^;]*).*$)|^.*$/, "$1"); | ||
}, | ||
|
||
setAccessToken: (token, days = 1) => { | ||
const expires = new Date(Date.now() + days * 864e5).toUTCString(); | ||
document.cookie = `ACCESS=${encodeURIComponent(token)}; expires=${expires}; path=/`; | ||
}, | ||
|
||
getRefreshToken: () => { | ||
return localStorage.getItem('REFRESH'); | ||
}, | ||
|
||
setRefreshToken: (token) => { | ||
localStorage.setItem('REFRESH', token); | ||
}, | ||
|
||
isTokenExpired: async (token) => { | ||
try { | ||
const response = await axios.post(`${AUTH_BASE_URL}/api/mypage`, null, { | ||
headers: { 'Authorization': `Bearer ${token}` } | ||
}); | ||
return response.status !== 200; | ||
} catch (error) { | ||
console.error('Error validating token:', error); | ||
return true; | ||
} | ||
}, | ||
|
||
refreshToken: async () => { | ||
const refreshToken = OAuthSDK.getRefreshToken(); | ||
if (refreshToken) { | ||
try { | ||
const response = await axios.post(`${AUTH_BASE_URL}/api/oauth/refresh`, { refresh_token: refreshToken }); | ||
const newAccessToken = response.data.access_token; | ||
OAuthSDK.setAccessToken(newAccessToken); | ||
return newAccessToken; | ||
} catch (error) { | ||
console.error('Token refresh failed:', error); | ||
return null; | ||
} | ||
} | ||
return null; | ||
}, | ||
|
||
checkAuthentication: async () => { | ||
const accessToken = OAuthSDK.getAccessToken(); | ||
if (accessToken) { | ||
const isExpired = await OAuthSDK.isTokenExpired(accessToken); | ||
if (isExpired) { | ||
return await OAuthSDK.refreshToken(); | ||
} | ||
return accessToken; | ||
} | ||
return await OAuthSDK.refreshToken(); | ||
}, | ||
|
||
logout: () => { | ||
document.cookie = 'ACCESS=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; | ||
localStorage.removeItem('REFRESH'); | ||
}, | ||
|
||
setTokens: (accessToken, refreshToken, days = 1) => { | ||
OAuthSDK.setAccessToken(accessToken, days); | ||
OAuthSDK.setRefreshToken(refreshToken); | ||
} | ||
}; | ||
|
||
export default OAuthSDK; |
Oops, something went wrong.