mirror of
https://github.com/tubearchivist/tubearchivist.git
synced 2025-05-09 21:01:10 +00:00
serialize user, add account endpoint
This commit is contained in:
parent
5498958b36
commit
5772ab3e68
@ -1,5 +1,7 @@
|
|||||||
"""serializer for account model"""
|
"""serializer for account model"""
|
||||||
|
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from user.models import Account
|
from user.models import Account
|
||||||
|
|
||||||
@ -18,3 +20,45 @@ class AccountSerializer(serializers.ModelSerializer):
|
|||||||
"user_permissions",
|
"user_permissions",
|
||||||
"last_login",
|
"last_login",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UserMeConfigSerializer(serializers.Serializer):
|
||||||
|
"""serialize user me config"""
|
||||||
|
|
||||||
|
stylesheet = serializers.CharField()
|
||||||
|
page_size = serializers.IntegerField()
|
||||||
|
sort_by = serializers.ChoiceField(
|
||||||
|
choices=[
|
||||||
|
"published",
|
||||||
|
"downloaded",
|
||||||
|
"views",
|
||||||
|
"likes",
|
||||||
|
"duration",
|
||||||
|
"filesize",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
sort_order = serializers.ChoiceField(choices=["asc", "desc"])
|
||||||
|
view_style_home = serializers.ChoiceField(choices=["grid", "list"])
|
||||||
|
view_style_channel = serializers.ChoiceField(choices=["grid", "list"])
|
||||||
|
view_style_downloads = serializers.ChoiceField(choices=["grid", "list"])
|
||||||
|
view_style_playlist = serializers.ChoiceField(choices=["grid", "list"])
|
||||||
|
grid_items = serializers.IntegerField()
|
||||||
|
hide_watched = serializers.BooleanField()
|
||||||
|
show_ignored_only = serializers.BooleanField()
|
||||||
|
show_subed_only = serializers.BooleanField()
|
||||||
|
show_help_text = serializers.BooleanField()
|
||||||
|
|
||||||
|
def __init__(self, *args, required=True, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Override 'required' for all fields if provided
|
||||||
|
for field in self.fields.values():
|
||||||
|
field.required = required
|
||||||
|
|
||||||
|
|
||||||
|
class LoginSerializer(serializers.Serializer):
|
||||||
|
"""serialize login"""
|
||||||
|
|
||||||
|
username = serializers.CharField()
|
||||||
|
password = serializers.CharField()
|
||||||
|
remember_me = serializers.ChoiceField(choices=["on", "off"], default="off")
|
||||||
|
@ -143,6 +143,16 @@ class UserConfig:
|
|||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
def update_config(self, to_update: dict) -> None:
|
||||||
|
"""update config object"""
|
||||||
|
data = {"doc": {"config": to_update}}
|
||||||
|
response, status = ElasticWrap(self.es_update_url).post(data)
|
||||||
|
if status < 200 or status > 299:
|
||||||
|
raise ValueError(f"Failed storing user value {status}: {response}")
|
||||||
|
|
||||||
|
for key, value in to_update.items():
|
||||||
|
print(f"User {self._user_id} value '{key}' change: to {value}")
|
||||||
|
|
||||||
def sync_defaults(self):
|
def sync_defaults(self):
|
||||||
"""set initial defaults on 404"""
|
"""set initial defaults on 404"""
|
||||||
response, _ = ElasticWrap(self.es_url).post(
|
response, _ = ElasticWrap(self.es_url).post(
|
||||||
|
@ -6,5 +6,6 @@ from user import views
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("login/", views.LoginApiView.as_view(), name="api-user-login"),
|
path("login/", views.LoginApiView.as_view(), name="api-user-login"),
|
||||||
path("logout/", views.LogoutApiView.as_view(), name="api-user-logout"),
|
path("logout/", views.LogoutApiView.as_view(), name="api-user-logout"),
|
||||||
|
path("account/", views.UserAccountView.as_view(), name="api-user-account"),
|
||||||
path("me/", views.UserConfigView.as_view(), name="api-user-me"),
|
path("me/", views.UserConfigView.as_view(), name="api-user-me"),
|
||||||
]
|
]
|
||||||
|
@ -1,63 +1,75 @@
|
|||||||
"""all user api views"""
|
"""all user api views"""
|
||||||
|
|
||||||
|
from common.serializers import ErrorResponseSerializer
|
||||||
from common.views import ApiBaseView
|
from common.views import ApiBaseView
|
||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from user.models import Account
|
from user.models import Account
|
||||||
from user.serializers import AccountSerializer
|
from user.serializers import (
|
||||||
|
AccountSerializer,
|
||||||
|
LoginSerializer,
|
||||||
|
UserMeConfigSerializer,
|
||||||
|
)
|
||||||
from user.src.user_config import UserConfig
|
from user.src.user_config import UserConfig
|
||||||
|
|
||||||
|
|
||||||
|
class UserAccountView(ApiBaseView):
|
||||||
|
"""resolves to /api/user/account/
|
||||||
|
GET: return current user account
|
||||||
|
"""
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses=AccountSerializer(),
|
||||||
|
)
|
||||||
|
def get(self, request):
|
||||||
|
"""get user account"""
|
||||||
|
user_id = request.user.id
|
||||||
|
account = Account.objects.get(id=user_id)
|
||||||
|
account_serializer = AccountSerializer(account)
|
||||||
|
return Response(account_serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class UserConfigView(ApiBaseView):
|
class UserConfigView(ApiBaseView):
|
||||||
"""resolves to /api/user/me/
|
"""resolves to /api/user/me/
|
||||||
GET: return current user config
|
GET: return current user config
|
||||||
POST: update user config
|
POST: update user config
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@extend_schema(responses=UserMeConfigSerializer())
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""get config"""
|
"""get user config"""
|
||||||
user_id = request.user.id
|
config = UserConfig(request.user.id).get_config()
|
||||||
account = Account.objects.get(id=user_id)
|
serializer = UserMeConfigSerializer(config)
|
||||||
serializer = AccountSerializer(account)
|
|
||||||
response = serializer.data.copy()
|
|
||||||
|
|
||||||
config = UserConfig(user_id).get_config()
|
return Response(serializer.data)
|
||||||
response.update({"config": config})
|
|
||||||
|
|
||||||
return Response(response)
|
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
request=UserMeConfigSerializer(required=False),
|
||||||
|
responses={
|
||||||
|
200: UserMeConfigSerializer(),
|
||||||
|
400: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="Bad request"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""update config"""
|
"""update config, allows partial update"""
|
||||||
user_id = request.user.id
|
|
||||||
data = request.data
|
|
||||||
|
|
||||||
data_config = data.get("config")
|
data_serializer = UserMeConfigSerializer(
|
||||||
if not data_config:
|
data=request.data, required=False
|
||||||
message = {
|
)
|
||||||
"status": "Bad Request",
|
data_serializer.is_valid(raise_exception=True)
|
||||||
"message": "missing config key",
|
validated_data = data_serializer.validated_data
|
||||||
}
|
UserConfig(request.user.id).update_config(to_update=validated_data)
|
||||||
return Response(message, status=400)
|
config = UserConfig(request.user.id).get_config()
|
||||||
|
serializer = UserMeConfigSerializer(config)
|
||||||
|
|
||||||
user_conf = UserConfig(user_id)
|
return Response(serializer.data)
|
||||||
for key, value in data_config.items():
|
|
||||||
try:
|
|
||||||
user_conf.set_value(key, value)
|
|
||||||
except ValueError as err:
|
|
||||||
message = {
|
|
||||||
"status": "Bad Request",
|
|
||||||
"message": f"failed updating {key} to '{value}', {err}",
|
|
||||||
}
|
|
||||||
return Response(message, status=400)
|
|
||||||
|
|
||||||
response = user_conf.get_config()
|
|
||||||
response.update({"user_id": user_id})
|
|
||||||
|
|
||||||
return Response(response)
|
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name="dispatch")
|
@method_decorator(csrf_exempt, name="dispatch")
|
||||||
@ -67,21 +79,37 @@ class LoginApiView(APIView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
permission_classes = [AllowAny]
|
permission_classes = [AllowAny]
|
||||||
|
SEC_IN_DAY = 60 * 60 * 24
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
request=LoginSerializer(),
|
||||||
|
responses={204: OpenApiResponse(description="login successful")},
|
||||||
|
)
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""post data"""
|
"""login with username and password"""
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
|
data_serializer = LoginSerializer(data=request.data)
|
||||||
|
data_serializer.is_valid(raise_exception=True)
|
||||||
|
validated_data = data_serializer.validated_data
|
||||||
|
|
||||||
username = request.data.get("username")
|
username = validated_data["username"]
|
||||||
password = request.data.get("password")
|
password = validated_data["password"]
|
||||||
|
remember_me = validated_data.get("remember_me")
|
||||||
|
|
||||||
user = authenticate(request, username=username, password=password)
|
user = authenticate(request, username=username, password=password)
|
||||||
|
if user is None:
|
||||||
|
error = ErrorResponseSerializer({"error": "Invalid credentials"})
|
||||||
|
return Response(error.data, status=400)
|
||||||
|
|
||||||
|
if remember_me == "on":
|
||||||
|
request.session.set_expiry(self.SEC_IN_DAY * 365)
|
||||||
|
else:
|
||||||
|
request.session.set_expiry(self.SEC_IN_DAY * 2)
|
||||||
|
|
||||||
|
print(f"expire session in {request.session.get_expiry_age()} secs")
|
||||||
|
|
||||||
if user is not None:
|
|
||||||
login(request, user) # Creates a session for the user
|
login(request, user) # Creates a session for the user
|
||||||
return Response({"message": "Login successful"}, status=200)
|
return Response(status=204)
|
||||||
|
|
||||||
return Response({"message": "Invalid credentials"}, status=400)
|
|
||||||
|
|
||||||
|
|
||||||
class LogoutApiView(ApiBaseView):
|
class LogoutApiView(ApiBaseView):
|
||||||
@ -89,7 +117,10 @@ class LogoutApiView(ApiBaseView):
|
|||||||
POST: handle logout
|
POST: handle logout
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses={204: OpenApiResponse(description="logout successful")}
|
||||||
|
)
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""logout on post request"""
|
"""logout user from session"""
|
||||||
logout(request)
|
logout(request)
|
||||||
return Response({"message": "Successfully logged out."}, status=200)
|
return Response(status=204)
|
||||||
|
@ -53,8 +53,8 @@ class SponsorBlock:
|
|||||||
print(f"{youtube_id}: get sponsorblock timestamps")
|
print(f"{youtube_id}: get sponsorblock timestamps")
|
||||||
try:
|
try:
|
||||||
response = requests.get(url, headers=headers, timeout=10)
|
response = requests.get(url, headers=headers, timeout=10)
|
||||||
except requests.ReadTimeout:
|
except (requests.ReadTimeout, requests.ConnectionError) as err:
|
||||||
print(f"{youtube_id}: sponsorblock API timeout")
|
print(f"{youtube_id}: sponsorblock API error: {str(err)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not response.ok:
|
if not response.ok:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user