handle logout, #855

This commit is contained in:
Simon 2025-01-01 15:26:23 +07:00
parent 20e3b7b7ff
commit 86232b8628
No known key found for this signature in database
GPG Key ID: 2C15AA5E89985DD4
5 changed files with 47 additions and 5 deletions

View File

@ -5,5 +5,6 @@ from user import views
urlpatterns = [
path("login/", views.LoginApiView.as_view(), name="api-user-login"),
path("logout/", views.LogoutApiView.as_view(), name="api-user-logout"),
path("me/", views.UserConfigView.as_view(), name="api-user-me"),
]

View File

@ -1,7 +1,7 @@
"""all user api views"""
from common.views import ApiBaseView
from django.contrib.auth import authenticate, login
from django.contrib.auth import authenticate, login, logout
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from rest_framework.permissions import AllowAny
@ -82,3 +82,14 @@ class LoginApiView(APIView):
return Response({"message": "Login successful"}, status=200)
return Response({"message": "Invalid credentials"}, status=400)
class LogoutApiView(ApiBaseView):
"""resolves to /api/user/logout/
POST: handle logout
"""
def post(self, request, *args, **kwargs):
"""logout on post request"""
logout(request)
return Response({"message": "Successfully logged out."}, status=200)

View File

@ -0,0 +1,22 @@
import defaultHeaders from '../../configuration/defaultHeaders';
import getApiUrl from "../../configuration/getApiUrl"
import getFetchCredentials from "../../configuration/getFetchCredentials";
import getCookie from "../../functions/getCookie";
const logOut = async () => {
const apiUrl = getApiUrl();
const csrfCookie = getCookie('csrftoken');
const response = await fetch(`${apiUrl}/api/user/logout/`, {
method: 'POST',
headers: {
...defaultHeaders,
'X-CSRFToken': csrfCookie || '',
},
credentials: getFetchCredentials(),
})
return response;
}
export default logOut;

View File

@ -1,15 +1,24 @@
import { Link } from 'react-router-dom';
import { Link, useNavigate } from 'react-router-dom';
import iconSearch from '/img/icon-search.svg';
import iconGear from '/img/icon-gear.svg';
import iconExit from '/img/icon-exit.svg';
import Routes from '../configuration/routes/RouteList';
import NavigationItem from './NavigationItem';
import logOut from '../api/actions/logOut';
interface NavigationProps {
isAdmin: boolean;
}
const Navigation = ({ isAdmin }: NavigationProps) => {
const navigate = useNavigate();
const handleLogout = async (event: { preventDefault: () => void }) => {
event.preventDefault();
await logOut();
navigate(Routes.Login)
}
return (
<div className="boxed-content">
<Link to={Routes.Home}>
@ -30,9 +39,7 @@ const Navigation = ({ isAdmin }: NavigationProps) => {
<Link to={Routes.SettingsDashboard}>
<img src={iconGear} alt="gear-icon" title="Settings" />
</Link>
<Link to={Routes.Logout}>
<img className="alert-hover" src={iconExit} alt="exit-icon" title="Logout" />
</Link>
<img className="alert-hover" src={iconExit} alt="exit-icon" title="Logout" onClick={handleLogout} />
</div>
</div>
</div>

View File

@ -319,6 +319,7 @@ button:hover {
.alert-hover:hover {
filter: var(--img-filter-error);
cursor: pointer;
}
/* top of page */