Commit 4d69eb12 authored by Ilham Maulana's avatar Ilham Maulana 💻

fix: shit auth and book endpoint to not using drf

parent 6ea2daa0
import jwt
import json
import random import random
from django.utils import timezone
from django.contrib.auth import authenticate, login, logout from django.http import JsonResponse
from django.core.mail import send_mail from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import logout, authenticate
from rest_framework import views, viewsets, status
from rest_framework.response import Response
from rest_framework.filters import SearchFilter
from rest_framework.authtoken.models import Token
from users.models import ResetPasswordPin
from .serializers import (
User,
Librarian,
LibrarianSerializer,
LibrarianLoginHistory,
LoginHistorySerializer,
Member,
MemberSerializer,
User,
UserSerializer,
UpdateProfileSerializer,
)
from .permissions import IsStaffUser, IsNotStaffUser
class LibrarianViewSet(viewsets.ModelViewSet):
permission_classes = [IsStaffUser]
queryset = Librarian.objects.all().order_by("created_at")
serializer_class = LibrarianSerializer
filter_backends = [SearchFilter]
search_fields = [
"user__username",
"user__email",
"user__first_name",
"user__last_name",
]
def update(self, request, pk):
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
class LibrarianLoginHistoryViewSet(viewsets.ModelViewSet):
permission_classes = [IsStaffUser]
queryset = LibrarianLoginHistory.objects.all().order_by("date")
serializer_class = LoginHistorySerializer
filter_backends = [SearchFilter]
search_fields = ["librarian__name"]
class MemberViewSet(viewsets.ModelViewSet):
permission_classes = [IsNotStaffUser]
queryset = Member.objects.all().order_by("created_at")
serializer_class = MemberSerializer
filter_backends = [SearchFilter]
search_fields = [
"user__username",
"user__email",
"user__first_name",
"user__last_name",
]
def update(self, request, pk):
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
class UserDetailView(views.APIView):
def get(self, request):
header = request.headers.get("Authorization")
if header is None:
return Response(
{"message": "Unauthorized"}, status=status.HTTP_401_UNAUTHORIZED
)
token = header.split(" ")[1]
verified_token = Token.objects.filter(key=token)
if not verified_token.exists():
return Response(
{"message": "Token is invalid or expired"},
status=status.HTTP_401_UNAUTHORIZED,
)
user_id = verified_token[0].user.id
user = User.objects.get(pk=user_id)
account_id = None from users.models import User, Member, ResetPasswordPin
if user.is_staff: from loans.models import Book, BookLoan
account_id = user.librarian.id
else:
account_id = user.member.id
data = {
"id": user.pk,
"username": user.username,
"email": user.email,
"first_name": user.first_name,
"last_name": user.last_name,
"is_staff": user.is_staff,
"account_id": account_id,
}
return Response(data, status=status.HTTP_200_OK) @csrf_exempt
def loginUserView(request):
if request.method == "POST":
data = json.loads(request.body)
username = data.get("username")
password = data.get("password")
class LoginBaseView(views.APIView): user = authenticate(username=username, password=password)
if user is None:
return JsonResponse(
{"message": "Username or Password is incorrect"}, status=400
)
def post(self, request): expired = timezone.now() + timezone.timedelta(days=1)
user = authenticate( payload = {"user_id": user.pk, "exp": expired}
username=request.data["username"], password=request.data["password"] token = jwt.encode(payload, key="secret", algorithm="HS256")
return JsonResponse(
{"message": "Login successful", "token": token},
status=201,
) )
if user:
login(request=request, user=user)
token, created = Token.objects.get_or_create(user=user)
return Response({"token": token.key})
else:
return Response({"error": "Invalid credentials"}, status=401)
class LibrarianLoginView(LoginBaseView): @csrf_exempt
def registerUserView(request):
users = User.objects.all()
def post(self, request, *args, **kwargs): if request.method == "POST":
response = super().post(request, *args, **kwargs) data = json.loads(request.body)
username = data.get("username")
if response.status_code == 200: email = data.get("email")
if not request.user.is_staff: password = data.get("password")
return Response(
{"message": "Account does not have access"},
status=status.HTTP_403_FORBIDDEN,
)
else:
pass
return response is_username = users.filter(username=username)
is_email = users.filter(email=email)
if is_username.exists() or is_email.exists():
return JsonResponse(
{"message": "Username or email already exists"}, status=400
)
class RegisterBaseView(views.APIView): user = User.objects.create_user(
serializer_class = None username=username, email=email, password=password
)
expired = timezone.now() + timezone.timedelta(days=1)
payload = {"user_id": user.pk, "exp": expired}
token = jwt.encode(payload, key="secret", algorithm="HS256")
data = {
"message": "register successful",
"token": token,
}
return JsonResponse(data, status=201, safe=False)
def post(self, request):
data = request.data
serializer = self.serializer_class(data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
user = User.objects.get(id=serializer.data["id"]) @csrf_exempt
login(request=request, user=user) def logoutUserView(request):
token, created = Token.objects.get_or_create(user=user)
response = serializer.data.copy() if request.method == "GET":
response["token"] = token.key logout(request)
return Response(response, status=status.HTTP_200_OK) return JsonResponse({"message": "Logout successful"}, status=200)
return JsonResponse({"message": "Invalid request method"}, status=405)
class LibrarianRegisterView(RegisterBaseView):
serializer_class = UserSerializer
@csrf_exempt
def getUserDetail(request):
if request.method == "GET":
header_authorization = request.headers.get("Authorization")
try:
token = header_authorization.split(" ")[1]
payload = jwt.decode(token, key="secret", algorithms=["HS256"])
user = User.objects.get(pk=payload["user_id"])
data = {
"id": user.id,
"username": user.username,
"email": user.email,
"first_name": user.first_name,
"last_name": user.last_name,
"is_staff": user.is_staff,
}
return JsonResponse(data, status=200, safe=False)
except jwt.exceptions.InvalidTokenError:
return JsonResponse({"message": "Unauthorized"}, status=401)
else:
return JsonResponse({"message": "Invalid request method"}, status=405)
@csrf_exempt
def updateUserProfileView(request):
users = User.objects.all()
if request.method == "PUT":
data = json.loads(request.body)
header_authorization = request.headers.get("Authorization")
try:
token = header_authorization.split(" ")[1]
payload = jwt.decode(token, key="secret", algorithms=["HS256"])
user = User.objects.get(pk=payload["user_id"])
if data.get("username") != user.username:
is_username = users.filter(username=data.get("username"))
if is_username.exists():
return JsonResponse(
{"message": "Username already exists"}, status=400
)
if data.get("email") != user.email:
is_email = users.filter(email=data.get("email"))
if is_email.exists():
return JsonResponse({"message": "Email already exists"}, status=400)
user.username = data.get("username", user.username)
user.first_name = data.get("first_name", user.first_name)
user.last_name = data.get("last_name", user.last_name)
user.email = data.get("email", user.email)
user.save()
class MemberRegisterView(RegisterBaseView): return JsonResponse(
serializer_class = UserSerializer {"message": "User profile updated successfully"}, status=200
)
def post(self, request): except jwt.exceptions.InvalidTokenError:
response = super().post(request) return JsonResponse({"message": "Unauthorized"}, status=401)
user_id = response.data.get("id")
user = User.objects.get(pk=user_id)
Member.objects.create(user=user)
return response return JsonResponse({"message": "Invalid request method"}, status=405)
class MemberLoginView(LoginBaseView): @csrf_exempt
def changePasswordView(request):
if request.method == "PUT":
data = json.loads(request.body)
header_authorization = request.headers.get("Authorization")
def post(self, request, *args, **kwargs): try:
response = super().post(request, *args, **kwargs) token = header_authorization.split(" ")[1]
payload = jwt.decode(token, key="secret", algorithms=["HS256"])
user = User.objects.get(pk=payload["user_id"])
old_password = data.get("old_password")
new_password1 = data.get("new_password1")
new_password2 = data.get("new_password2")
if not user.check_password(old_password):
return JsonResponse(
{"message": "Invalid old password"},
status=400,
)
if response.status_code == 200: if new_password1 != new_password2:
if request.user.is_staff: return JsonResponse(
return Response( {"message": "Passwords and confirm password do not match"},
{"message": "Account does not have access"}, status=400,
status=status.HTTP_403_FORBIDDEN,
) )
else:
pass
return response user.set_password(str(new_password1))
user.save()
return JsonResponse({"message": "Change password successful"}, status=200)
class LogoutView(views.APIView): except jwt.exceptions.InvalidTokenError:
return JsonResponse({"message": "Unauthorized"}, status=401)
def get(self, request): return JsonResponse({"message": "Invalid request method"}, status=405)
header = request.headers.get("Authorization")
if header is None:
return Response(
{"message": "Unauthorized"}, status=status.HTTP_401_UNAUTHORIZED
)
token = header.split(" ")[1]
verified_token = Token.objects.filter(key=token)
if not verified_token.exists():
return Response(
{"message": "Token is invalid or expired"},
status=status.HTTP_401_UNAUTHORIZED,
)
verified_token.delete() def generate_random_pin():
logout(request=request) return random.randint(10000000, 99999999)
return Response({"message": "Logout success"}, status=status.HTTP_200_OK)
def store_data_with_pin(user):
is_pin = ResetPasswordPin.objects.filter(user=user).first()
if is_pin is None:
pin = generate_random_pin()
ResetPasswordPin.objects.create(pin=generate_random_pin(), user=user)
else:
pin = is_pin.pin
return pin
class MemberChangePasswordView(views.APIView):
permission_classes = [IsNotStaffUser]
def post(self, request, member_id): @csrf_exempt
new_password = request.data.get("new_password") def resetPasswordView(request):
old_password = request.data.get("old_password") users = User.objects.all()
member = Member.objects.get(pk=member_id)
user = member.user
if user.check_password(old_password): if request.method == "POST":
user.set_password(new_password) data = json.loads(request.body)
user.save() email = data.get("email")
return Response(
{"message": "Pasword succesfuly changed"}, status=status.HTTP_200_OK try:
user = users.get(email=email)
pin = store_data_with_pin(user)
message = f"Here's your reset password pin: {pin}"
user.email_user(
subject="Django Library App Reset password pin, dev: Ilham Maulana",
message=message,
from_email="from@example.com",
fail_silently=False,
)
return JsonResponse(
{"message": "Pin reset password sent successfully to your email"},
status=200,
)
except User.DoesNotExist:
return JsonResponse(
{"message": "User with this email does not exist"}, status=400
) )
return Response( return JsonResponse({"message": "Invalid request method"}, status=405)
{"message": "Change password failed, old password is invalid."},
status=status.HTTP_403_FORBIDDEN,
)
class TokenResetPasswordView(views.APIView): @csrf_exempt
def resetPasswordConfirmView(request):
if request.method == "POST":
data = json.loads(request.body)
password1 = data.get("password1")
password2 = data.get("password2")
pin = data.get("pin")
def generate_random_pin(self): if password1 is None or password2 is None:
return random.randint(10000000, 99999999) return JsonResponse(
{"message": "Password and confirm password are required"}, status=400
)
def store_data_with_pin(self, user): if password1 != password2:
pin = self.generate_random_pin() return JsonResponse(
ResetPasswordPin.objects.get_or_create(pin=pin, user=user) {"message": "Password and confirm password do not match"}, status=400
return pin )
def post(self, request): if pin is None:
data = request.data.copy() return JsonResponse({"message": "Pin is required"}, status=400)
email = data.get("email")
user = User.objects.get(email=email)
if user is None: pin = ResetPasswordPin.objects.filter(pin=pin).first()
return Response( if pin is None:
{"message": "Invalid Email, Request pin reset password failed"}, return JsonResponse({"message": "Pin is invalid"}, status=400)
status=status.HTTP_403_FORBIDDEN,
)
pin = self.store_data_with_pin(user) if password1 != password2:
message = f"Here's your reset password pin: {pin}" return JsonResponse(
send_mail( {"message": "Passwords and confirm password do not match"},
subject="Django Library App Reset password pin, dev: Ilham Maulana", status=400,
message=message, )
from_email="from@example.com",
recipient_list=["to@example.com"],
fail_silently=False,
)
data["message"] = ( pin.user.set_password(password1)
"Your pin request was successful! We've sent an email with instructions on how to use it." pin.delete()
) return JsonResponse({"message": "Password reset successful"}, status=200)
return Response(data, status=status.HTTP_200_OK) return JsonResponse({"message": "Invalid request method"}, status=405)
class ResetPasswordConfirmView(views.APIView): @csrf_exempt
def checkAuthSessionView(request):
if request.method == "GET":
if request.user.is_authenticated:
return JsonResponse(
{"message": "User is authenticated", "authenticated": True}, status=200
)
else:
return JsonResponse(
{"message": "User is not authenticated", "authenticated": False},
status=401,
)
else:
return JsonResponse({"message": "Invalid request method"}, status=405)
def post(self, request):
data = request.data
pin = data.get("pin") @csrf_exempt
password1 = data.get("password1") def memberLoanView(request):
password2 = data.get("password2") header_authorization = request.headers.get("Authorization")
encoded = None book_loans = BookLoan.objects.all()
is_password_invalid = password1 != password2 if request.method == "GET":
if is_password_invalid: now = timezone.now()
return Response( due_date_treshold = now + timezone.timedelta(days=3)
{"message": "password and confirm password are not same"}, near_outstanding = request.GET.get("near_outstanding")
status=status.HTTP_400_BAD_REQUEST, overdue = request.GET.get("overdue")
)
try: try:
encoded = ResetPasswordPin.objects.get(pin=pin) token = header_authorization.split(" ")[1]
except ResetPasswordPin.DoesNotExist: payload = jwt.decode(token, key="secret", algorithms=["HS256"])
return Response( user = User.objects.get(pk=payload["user_id"])
{"message": "Invalid pin reset password"}, member = Member.objects.filter(user=user).first()
status=status.HTTP_401_UNAUTHORIZED,
) if member is None:
return JsonResponse({"message": "Member not found"}, status=404)
is_loans = book_loans.filter(member=member).first()
if is_loans is None:
return JsonResponse({"message": "No loans found"}, status=404)
loans = book_loans.filter(member=member)
if near_outstanding:
loans = (
loans.filter(due_date__lte=due_date_treshold, return_date=None)
.filter(due_date__gte=now)
.order_by("loan_date")
)
encoded.user.set_password(password1) if overdue:
encoded.user.save() loans = loans.filter(due_date__lte=now, return_date=None).order_by(
encoded.delete() "loan_date"
)
return Response( data = []
{"message": "Reset password success"}, for loan in loans:
status=status.HTTP_200_OK, remaining_loan_time = str(loan.due_date.day - now.day) + " days left"
) is_overdue = loan.due_date < now
loan_obj = {
"book": {
"id": loan.book.id,
"title": loan.book.title,
"author": loan.book.author,
"description": loan.book.description,
"cover_image": loan.book.cover_image.url,
},
"remaining_loan_time": remaining_loan_time,
"is_overdue": is_overdue,
"loan_date": loan.loan_date,
"due_date": loan.due_date,
}
data.append(loan_obj)
book_loans.filter(due_date__lte=now, return_date=None).order_by("loan_date")
return JsonResponse(data, safe=False, status=200)
except jwt.exceptions.InvalidTokenError:
return JsonResponse({"message": "Unauthorized"}, status=401)
if request.method == "POST":
data = json.loads(request.body)
book_id = data.get("book")
member_id = data.get("member")
loan_date = data.get("loan_date")
due_date = data.get("due_date")
try:
token = header_authorization.split(" ")[1]
jwt.decode(token, key="secret", algorithms=["HS256"])
member = Member.objects.filter(user__pk=member_id).first()
book = Book.objects.filter(pk=book_id).first()
class UpdateProfileView(viewsets.ModelViewSet): if member is None:
serializer_class = UpdateProfileSerializer return JsonResponse({"message": "Member not found"}, status=404)
queryset = User.objects.all().order_by("id")
def update(self, request, pk): if book is None:
instance = self.get_object() return JsonResponse({"message": "Book not found"}, status=404)
serializer = self.get_serializer(instance, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
user_id = serializer.data.get("id") book_loans.create(
user = User.objects.get(pk=user_id) member=member, book=book, loan_date=loan_date, due_date=due_date
)
return JsonResponse(
{"message": "create loan successfull"}, safe=False, status=200
)
account_id = None except jwt.exceptions.InvalidTokenError:
if user.is_staff: return JsonResponse({"message": "Unauthorized"}, status=401)
account_id = user.librarian.id
else:
account_id = user.member.id
response = serializer.data return JsonResponse({"message": "Invalid request method"}, status=405)
response["account_id"] = account_id
return Response(response, status=status.HTTP_200_OK)
import json
from django.http import JsonResponse
from django.core.serializers import serialize
from django.views.decorators.csrf import csrf_exempt
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.filters import SearchFilter from rest_framework.filters import SearchFilter
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from .serializers import Book, BookSerializer, Category, CategorySerializer
from book.models import Book, Category
from .serializers import BookSerializer, CategorySerializer
@csrf_exempt
def bookView(request):
books = Book.objects.all().order_by("created_at")
category = request.GET.get("category")
keyword = request.GET.get("search")
if request.method == "GET":
if category:
books = books.filter(category__name=category)
if keyword and len(keyword) >= 3:
books = books.filter(title__icontains=keyword)
data = []
for book_item in books:
if book_item.category is not None:
book = {
"id": book_item.id,
"title": book_item.title,
"author": book_item.author,
"description": book_item.description,
"cover_image": "http://127.0.0.1:8000" + book_item.cover_image.url,
"category": {
"name": book_item.category.name,
},
}
book = {
"id": book_item.id,
"title": book_item.title,
"author": book_item.author,
"description": book_item.description,
"cover_image": "http://127.0.0.1:8000" + book_item.cover_image.url,
}
data.append(book)
return JsonResponse(data, status=200, safe=False)
return JsonResponse({"message": "Invalid request method"}, status=405)
class BookViewSet(viewsets.ModelViewSet): class BookViewSet(viewsets.ModelViewSet):
......
...@@ -2,22 +2,19 @@ from django.urls import path, include ...@@ -2,22 +2,19 @@ from django.urls import path, include
from rest_framework import routers from rest_framework import routers
from .auth.views import ( from .auth.views import (
LibrarianViewSet, registerUserView,
LibrarianLoginView, loginUserView,
LibrarianRegisterView, logoutUserView,
LibrarianLoginHistoryViewSet, getUserDetail,
MemberViewSet, memberLoanView,
MemberLoginView, updateUserProfileView,
MemberRegisterView, checkAuthSessionView,
MemberChangePasswordView, changePasswordView,
LogoutView, resetPasswordView,
TokenResetPasswordView, resetPasswordConfirmView,
ResetPasswordConfirmView,
UserDetailView,
UpdateProfileView,
LoginBaseView,
) )
from .book.views import BookViewSet, CategoryViewSet
from .book.views import bookView, CategoryViewSet
from .loans.views import ( from .loans.views import (
BookLoanViewSet, BookLoanViewSet,
OverduedBookLoanViewSet, OverduedBookLoanViewSet,
...@@ -27,10 +24,6 @@ from .loans.views import ( ...@@ -27,10 +24,6 @@ from .loans.views import (
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r"user", UpdateProfileView, basename="user")
router.register(r"librarians", LibrarianViewSet, basename="librarians")
router.register(r"members", MemberViewSet, basename="members")
router.register(r"books", BookViewSet, basename="books")
router.register(r"categories", CategoryViewSet, basename="categories") router.register(r"categories", CategoryViewSet, basename="categories")
router.register(r"book-loans", BookLoanViewSet, basename="book_loans") router.register(r"book-loans", BookLoanViewSet, basename="book_loans")
router.register( router.register(
...@@ -39,9 +32,6 @@ router.register( ...@@ -39,9 +32,6 @@ router.register(
router.register( router.register(
r"upcoming-loans", UpComingBookLoanViewSet, basename="book_loans_upcoming" r"upcoming-loans", UpComingBookLoanViewSet, basename="book_loans_upcoming"
) )
router.register(
r"login-history", LibrarianLoginHistoryViewSet, basename="librarian_login_history"
)
router_member_loan = routers.DefaultRouter() router_member_loan = routers.DefaultRouter()
router_member_loan.register(r"loans", MemberLoanViewSet, basename="member_loans") router_member_loan.register(r"loans", MemberLoanViewSet, basename="member_loans")
...@@ -49,49 +39,20 @@ router_member_loan.register(r"loans", MemberLoanViewSet, basename="member_loans" ...@@ -49,49 +39,20 @@ router_member_loan.register(r"loans", MemberLoanViewSet, basename="member_loans"
urlpatterns = [ urlpatterns = [
path("", include(router.urls)), path("", include(router.urls)),
# auth # auth
path( path("user", getUserDetail, name="user_detail"),
"user", path("user/loans", memberLoanView, name="user_loans"),
UserDetailView.as_view(), path("user/update", updateUserProfileView, name="update_user_profile"),
name="user_detail", path("auth/login", loginUserView, name="login"),
), path("auth/logout", logoutUserView, name="logout"),
path( path("auth/register", registerUserView, name="register"),
"user", path("auth/change-password", changePasswordView, name="change_password"),
UserDetailView.as_view(), path("auth/reset-password", resetPasswordView, name="reset_password"),
name="user_detail", path(
), "auth/reset-password-confirm",
path( resetPasswordConfirmView,
"reset-password/request-token",
TokenResetPasswordView.as_view(),
name="reset_password_request_token",
),
path(
"reset-password/confirm",
ResetPasswordConfirmView.as_view(),
name="reset_password_confirm", name="reset_password_confirm",
), ),
path("librarians/auth/login", LibrarianLoginView.as_view(), name="librarian_login"), path("auth/check-auth-session", checkAuthSessionView, name="check_auth_session"),
path("auth/login", LoginBaseView.as_view(), name="universal_login"), # books
path( path("books", bookView, name="books"),
"librarians/auth/register",
LibrarianRegisterView.as_view(),
name="librarian_register",
),
path("auth/logout", LogoutView.as_view(), name="librarian_logout"),
path("members/auth/login", MemberLoginView.as_view(), name="member_login"),
path(
"members/auth/register",
MemberRegisterView.as_view(),
name="librarian_register",
),
# change password
path(
"members/<int:member_id>/change-password",
MemberChangePasswordView.as_view(),
name="member_change_password",
),
path(
"members/<int:member_id>/",
include(router_member_loan.urls),
name="member_loans",
),
] ]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment