Added profiles page and friends system
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import bcrypt
|
||||||
import jwt
|
import jwt
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from rest_framework.authentication import (
|
from rest_framework.authentication import (
|
||||||
@@ -30,6 +31,13 @@ class JWTAuthentication(BaseAuthentication):
|
|||||||
)
|
)
|
||||||
|
|
||||||
user = Profile.objects.get(id=payload["id"])
|
user = Profile.objects.get(id=payload["id"])
|
||||||
|
|
||||||
|
if not bcrypt.checkpw(
|
||||||
|
payload["password"].encode("utf-8"),
|
||||||
|
user.password.encode("utf-8"),
|
||||||
|
):
|
||||||
|
error = "Token has expired"
|
||||||
|
raise AuthenticationFailed(error)
|
||||||
except Profile.DoesNotExist:
|
except Profile.DoesNotExist:
|
||||||
error = "Invalid token"
|
error = "Invalid token"
|
||||||
raise AuthenticationFailed(error) from None
|
raise AuthenticationFailed(error) from None
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 4.2.10 on 2024-03-02 10:14
|
# Generated by Django 4.2.10 on 2024-03-03 06:12
|
||||||
|
|
||||||
import api.users.validators
|
import api.users.validators
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
@@ -24,6 +24,7 @@ class Migration(migrations.Migration):
|
|||||||
('isPublic', models.BooleanField()),
|
('isPublic', models.BooleanField()),
|
||||||
('phone', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.MaxLengthValidator(20), django.core.validators.RegexValidator('\\+[\\d]+')])),
|
('phone', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.MaxLengthValidator(20), django.core.validators.RegexValidator('\\+[\\d]+')])),
|
||||||
('image', models.URLField(blank=True, null=True)),
|
('image', models.URLField(blank=True, null=True)),
|
||||||
|
('friends', models.ManyToManyField(blank=True, to='users.profile')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class Profile(models.Model):
|
|||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
image = models.URLField(max_length=200, blank=True, null=True)
|
image = models.URLField(max_length=200, blank=True, null=True)
|
||||||
|
friends = models.ManyToManyField("self", blank=True, symmetrical=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.login
|
return self.login
|
||||||
@@ -39,6 +40,16 @@ class Profile(models.Model):
|
|||||||
def is_authenticated(self):
|
def is_authenticated(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def add_friend(self, user):
|
||||||
|
if self != user:
|
||||||
|
self.friends.add(user)
|
||||||
|
|
||||||
|
def remove_friend(self, user):
|
||||||
|
self.friends.remove(user)
|
||||||
|
|
||||||
|
def check_for_friendship(self, user):
|
||||||
|
return self.friends.filter(pk=user.pk).exists()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_unique(cls, user_id, validated_data):
|
def check_unique(cls, user_id, validated_data):
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from rest_framework.permissions import BasePermission
|
||||||
|
|
||||||
|
|
||||||
|
class CanAccessProfile(BasePermission):
|
||||||
|
def has_object_permission(self, request, view, obj):
|
||||||
|
if (
|
||||||
|
obj.isPublic
|
||||||
|
or obj.check_for_friendship(request.user)
|
||||||
|
or obj == request.user
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
@@ -28,3 +28,24 @@ class UpdateProfileSerializer(serializers.ModelSerializer):
|
|||||||
"phone",
|
"phone",
|
||||||
"image",
|
"image",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PublicProfileSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Profile
|
||||||
|
fields = [
|
||||||
|
"login",
|
||||||
|
"email",
|
||||||
|
"countryCode",
|
||||||
|
"isPublic",
|
||||||
|
"phone",
|
||||||
|
"image",
|
||||||
|
]
|
||||||
|
|
||||||
|
def to_representation(self, instance):
|
||||||
|
data = super().to_representation(instance)
|
||||||
|
if data["image"] is None:
|
||||||
|
del data["image"]
|
||||||
|
if data["phone"] is None:
|
||||||
|
del data["phone"]
|
||||||
|
return data
|
||||||
|
|||||||
@@ -16,5 +16,21 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"me/profile",
|
"me/profile",
|
||||||
api.users.views.ProfileMeApiView.as_view(),
|
api.users.views.ProfileMeApiView.as_view(),
|
||||||
)
|
name="profile-me",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"profiles/<str:login>",
|
||||||
|
api.users.views.ProfileAPIView.as_view(),
|
||||||
|
name="profiles",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"friends/add",
|
||||||
|
api.users.views.AddFriendAPIView.as_view(),
|
||||||
|
name="add-friend",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"friends/remove",
|
||||||
|
api.users.views.RemoveFriendAPIView.as_view(),
|
||||||
|
name="remove-friend",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,7 +10,12 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from api.users.models import Profile
|
from api.users.models import Profile
|
||||||
from api.users.serializers import ProfileSerializer, UpdateProfileSerializer
|
from api.users.permissions import CanAccessProfile
|
||||||
|
from api.users.serializers import (
|
||||||
|
ProfileSerializer,
|
||||||
|
PublicProfileSerializer,
|
||||||
|
UpdateProfileSerializer,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RegisterUserApiView(APIView):
|
class RegisterUserApiView(APIView):
|
||||||
@@ -116,3 +121,57 @@ class ProfileMeApiView(APIView):
|
|||||||
profile["image"] = user.image
|
profile["image"] = user.image
|
||||||
|
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileAPIView(APIView):
|
||||||
|
permission_classes = [IsAuthenticated, CanAccessProfile]
|
||||||
|
|
||||||
|
def get(self, request, login):
|
||||||
|
try:
|
||||||
|
profile = Profile.objects.get(login=login)
|
||||||
|
self.check_object_permissions(request, profile)
|
||||||
|
serializer = PublicProfileSerializer(profile)
|
||||||
|
return Response(serializer.data)
|
||||||
|
except Profile.DoesNotExist:
|
||||||
|
return Response(
|
||||||
|
{"detail": "Profile not found."},
|
||||||
|
status=status.HTTP_403_FORBIDDEN,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AddFriendAPIView(APIView):
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
try:
|
||||||
|
login = request.data.get("login")
|
||||||
|
profile = Profile.objects.get(login=login)
|
||||||
|
request.user.add_friend(profile)
|
||||||
|
return Response(
|
||||||
|
{"status": "ok"},
|
||||||
|
status=status.HTTP_200_OK,
|
||||||
|
)
|
||||||
|
except Profile.DoesNotExist:
|
||||||
|
return Response(
|
||||||
|
{"detail": "Profile not found."},
|
||||||
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveFriendAPIView(APIView):
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
try:
|
||||||
|
login = request.data.get("login")
|
||||||
|
profile = Profile.objects.get(login=login)
|
||||||
|
request.user.remove_friend(profile)
|
||||||
|
return Response(
|
||||||
|
{"status": "ok"},
|
||||||
|
status=status.HTTP_200_OK,
|
||||||
|
)
|
||||||
|
except Profile.DoesNotExist:
|
||||||
|
return Response(
|
||||||
|
{"detail": "Profile not found."},
|
||||||
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user