diff --git a/.gitignore b/.gitignore index d33774a..5d1f529 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ __pycache__ .idea db.sqlite3 -.vscode \ No newline at end of file +.vscode +.python-version \ No newline at end of file diff --git a/apps/accounts/api/mobile/__init__.py b/apps/accounts/api/mobile/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/accounts/api/mobile/serializers.py b/apps/accounts/api/mobile/serializers.py new file mode 100644 index 0000000..a5261d4 --- /dev/null +++ b/apps/accounts/api/mobile/serializers.py @@ -0,0 +1,95 @@ +from rest_framework import serializers +from django.contrib.auth import get_user_model +from django.db.models import Q +from rest_framework_jwt.settings import api_settings + + +User = get_user_model() +jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER +jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER + + +class UserDetailSerializer(serializers.ModelSerializer): + name = serializers.SerializerMethodField() + token = serializers.SerializerMethodField() + roles = serializers.SerializerMethodField() + avatar = serializers.SerializerMethodField() + + class Meta: + model = User + fields = [ + 'username', + 'name', + 'email', + 'roles', + 'introduction', + 'avatar', + 'token' + ] + + def get_name(self, obj): + return obj.username + + def get_token(self, obj): + return obj.username + + def get_roles(self, obj): + return obj.role.split() + + def get_avatar(self, obj): + return obj.get_image_url() + + +class UserLoginSerializer(serializers.ModelSerializer): + token = serializers.CharField(allow_blank=True, read_only=True) + username = serializers.CharField(required=False, allow_blank=True) + + # email = serializers.EmailField(required=False, allow_blank=True) + + class Meta: + model = User + fields = [ + 'username', + # 'email', + 'password', + 'token' + ] + + extra_kwargs = { + 'password': { + 'write_only': True + } + } + + def validate(self, data): + user_obj = None + username = data.get('username', None) + # email = data.get('email', None) + password = data.get('password', None) + + # if not email and not username: + # raise serializers.ValidationError('username or email is required to login.') + # user = User.objects.filter( + # Q(username=username) | + # Q(email=email) + # ).distinct() + if not username: + raise serializers.ValidationError('username or email is required to login.') + user = User.objects.filter( + Q(username=username) | + Q(email=username) + ).distinct() + user = user.exclude(email__isnull=True).exclude(email__iexact='') + if user.exists() and user.count() == 1: + user_obj = user.first() + else: + raise serializers.ValidationError('This username/email is not valid.') + + if user_obj: + payload = jwt_payload_handler(user_obj) + token = jwt_encode_handler(payload) + if not user_obj.check_password(password): + raise serializers.ValidationError('Incorrect credentials pls try again.') + data['token'] = token + + return data diff --git a/apps/accounts/api/mobile/urls.py b/apps/accounts/api/mobile/urls.py new file mode 100644 index 0000000..78d9b66 --- /dev/null +++ b/apps/accounts/api/mobile/urls.py @@ -0,0 +1,10 @@ +from django.urls import path +from .views import UserLoginAPIView, UserDetailAPIView, UserLogoutAPIView + + +app_name = 'users-api' +urlpatterns = [ + path('login/', UserLoginAPIView.as_view(), name='login'), + path('logout/', UserLogoutAPIView.as_view(), name='logout'), + path('info/', UserDetailAPIView.as_view(), name='info'), +] \ No newline at end of file diff --git a/apps/accounts/api/mobile/views.py b/apps/accounts/api/mobile/views.py new file mode 100644 index 0000000..16f82cf --- /dev/null +++ b/apps/accounts/api/mobile/views.py @@ -0,0 +1,74 @@ +from django.contrib.auth import get_user_model +from django.db.models import Q +from rest_framework import serializers +from rest_framework.response import Response +from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_400_BAD_REQUEST +from rest_framework.views import APIView +from rest_framework.generics import RetrieveAPIView +from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer +from rest_framework.views import exception_handler +from .serializers import UserLoginSerializer, UserDetailSerializer +from django.contrib.auth.backends import ModelBackend + +User = get_user_model() + + +class CustomBackend(ModelBackend): + def authenticate(self, request, username=None, password=None, **kwargs): + try: + user = User.objects.get(Q(username=username)|Q(email=username)) + if user.check_password(password): + return user + except Exception as e: + return None + + +class UserLoginAPIView(APIView): + permission_classes = [AllowAny] + serializer_class = UserLoginSerializer + + def post(self, request, *args, **kwargs): + data = request.data + serializer = UserLoginSerializer(data=data) + if serializer.is_valid(raise_exception=True): + login_data = serializer.data + response_data = { + "data": { + "username": login_data['username'], + "token": login_data['token'] + }, + "code": 0, + "message": "success" + } + return Response(response_data, HTTP_200_OK) + else: + return Response(serializer.errors, HTTP_400_BAD_REQUEST) + + +class UserLogoutAPIView(APIView): + permission_classes = [IsAuthenticated] + serializer_class = UserLoginSerializer + + def get(self, request, *args, **kwargs): + logout_data = {'msg': 'logout successfully'} + return Response(logout_data, HTTP_200_OK) + + +class UserDetailAPIView(RetrieveAPIView): + serializer_class = UserDetailSerializer + permission_classes = [AllowAny] + queryset = User.objects.all() + + def get(self, request, *args, **kwargs): + token = request.GET.get('token', None) + data = {'token': token} + try: + valid_data = VerifyJSONWebTokenSerializer().validate(data) + user = valid_data['user'] + serializer = self.get_serializer(user) + return Response(serializer.data, HTTP_200_OK) + except serializers.ValidationError as exc: + exc.detail = exc.detail[0] + response = exception_handler(exc, context=None) + return response diff --git a/apps/counter/api/views.py b/apps/counter/api/views.py index 4f6ee73..4a7763d 100644 --- a/apps/counter/api/views.py +++ b/apps/counter/api/views.py @@ -88,7 +88,7 @@ class DeviceInfoListAPIView(ListAPIView, RoleMixin): def get_queryset(self, *args, **kwargs): user_roles = self.get_role() - queryset = DeviceInfo.objects.all().order_by('-last_offline_time') + queryset = DeviceInfo.objects.get_queryset().order_by('-last_offline_time') if 'staff' in user_roles or 'manager' in user_roles: child = self.request.user.get_child() device_ids = [query.device_id diff --git a/mosqkiller/urls.py b/mosqkiller/urls.py index 7c5e6a3..e1be95c 100644 --- a/mosqkiller/urls.py +++ b/mosqkiller/urls.py @@ -29,6 +29,7 @@ urlpatterns = [ path('api/users/', include("accounts.api.urls")), path('api/login/', obtain_jwt_token), # path('api/docs/', include_docs_urls("Mosq API Doc")) + path('api/mobile/users/', include("accounts.api.mobile.urls")), ] urlpatterns += [